entity savedAndFlushed not found later in the same transaction - spring

I have a loop where I persist in DB some new objects if they are not already existing. (productPrice.product)
But in my loop, it does not find a previously persisted entity (with save and flush), and the re-creation of the entity triggers an DataIntegrityViolationException because my unique constraint is violated (ERROR: duplicate key value violates unique constraint "uk_product_b2c_ext_id")
Here is my code (simplified). I don't understand why it doesn't work.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void myFunction(List<ProductPriceCatalogResponseAPI.ProductPrice> productPrices) {
for (ProductPriceCatalogResponseAPI.ProductPrice productPrice : productPrices) {
ProductPriceB2C pp = new ProductPriceB2C();
pp.setProduct(productB2CRepository.findByExtId(productPrice.getProduct().getId())
.orElse(createNewProductFromExtId(productPrice.getProduct())));
productPriceB2CRepository.saveAndFlush(pp)
}
}
private ProductB2C createNewProductFromExtId(ProductPriceCatalogResponseAPI.Product product) {
ProductB2C p = new ProductB2C();
p.setName(product.getLabel());
p.setExtId(product.getId());
log.info("Persist product {} with external id {}", product.getLabel(), product.getId());
return productB2CRepository.saveAndFlush(p); // Exception here
}
ps : there is no cascade at all between ProductPrice and Product
Here is the code of my entities :
#Entity
#Table(name = "product_b2c")
public class ProductB2C {
#Id
#SequenceGenerator(name = "product_b2c_generator", sequenceName = "product_b2c_id_seq", allocationSize = 1, initialValue = 10000)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_b2c_generator")
private Long id;
#Column(name = "ext_id")
private Long extId;
#Column(name = "name")
private String name;
// getters and setters
}
#Entity
#Table(name = "product_price_b2c")
public class ProductPriceB2C extends AbstractAuditingEntity {
#Id
#SequenceGenerator(name = "product_price_b2c_generator", sequenceName = "product_price_b2c_id_seq", allocationSize = 1, initialValue = 10000)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_price_b2c_generator")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private ProductB2C product;
// other fields
// getters and setters
}

Related

Hibernate - Spring - ConstraintViolationException - UniqueConstraint

I'm trying to make some fixtures for my Profile model but every time I'm trying to save it "again" after I did an update, I get this message:
nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
This is my Profile class:
#Entity
#Data
#Builder
#ToString(of = {"birthday", "discordId", "description", "spokenLanguages"})
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "profile", uniqueConstraints = #UniqueConstraint(columnNames = "discordId"))
public class Profile implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idProfile;
private Date birthday;
#Column(name="discordId", insertable=true, updatable=false)
private String discordId;
private String description;
#ElementCollection(fetch = FetchType.EAGER)
private Set<String> spokenLanguages = new LinkedHashSet<String>();
#JsonIgnore
#OneToMany(fetch = FetchType.EAGER)
private Set<ProfileGame> profileGames = new LinkedHashSet<>();
#OneToOne(mappedBy = "profile", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
private User user;
#ManyToOne
private TimeSlot timeSlot;
}
Here is the call:
#Order(7)
#Test
void fillProfileGame() {
List<Profile> profileList = this.profileRepository.findAll();
for (Profile profile : profileList) {
List<Game> gameList = this.gameRepository.findAll();
Collections.shuffle(gameList);
int rndNbGame = new Random().ints(1, 5).findFirst().getAsInt();
for (int i = 1; i <= rndNbGame; i++) {
int rndLevel = new Random().ints(1, 100).findFirst().getAsInt();
int rndRanking = new Random().ints(1, 3000).findFirst().getAsInt();
Game rndGame = gameList.get(0);
gameList.remove(0);
ProfileGame profileGames = new ProfileGame(profile, rndGame, "level-" + rndLevel,
"ranking-" + rndRanking);
this.profileGameRepository.save(profileGames);
this.gameRepository.save(rndGame);
}
this.profileRepository.save(profile);
}
}
So what I understand is that Hibernate won't let me update this object because it has a unique contraint field ?
How do we proceed when we want a field to be unique and still being able to update other fields ?
From the code snippet, what I see is that there are some unique constraints applied on the column 'discordId'.
#Table(name = "profile", uniqueConstraints = #UniqueConstraint(columnNames = "discordId"))
and
#Column(name="discordId", insertable=true, updatable=false)
private String discordId;
As you can see, there is a parameter 'updatable' which is set to false. Therefore, when you are trying to update an already existing object, hibernate is throwing UniqueConstraintViolationException.
To fix this, set 'updatable=true' or remove it altogether and it should work fine.
#Column(name="discordId", insertable=true, updatable=true)
private String discordId;

Why does not it work SequenceGenerator in Spring Boot?

I make a blog on Spring Boot + Spring Date
There is a post, user, comment, and entities that contain links between them.
For each of these 6 entities, I added the annotation
#SequenceGenerator (name = "...", sequenceName = "...", allocationSize = 1)
Also created in the Database additionally hibernate_sequencе
However, the following problems arise.
When I add a post (with id = 1) and delete it, and then create a new post, it is already with id 2, not id 1
When I try to add a comment to it,then throws an error that usually occurs if there is no SequenceGenerator.
Error:
ERROR: insert or update on table "posts_comments" violates foreign key constraint "posts_comments_post_id_fkey"
DETAIL: Key (post_id) = (5) is not present in table
Why?
add comment in CommentService
public void create(Comments new_comment,Long parent_id, String login, int post_id)
{
Users user=userService.findByLogin(login);
Posts post=postsRepository.findById((long) post_id).get();
if((parent_id!=null)&&(commentsRepository.existsById(parent_id)))
{
Comments parentComment=commentsRepository.findById(parent_id).get();
parentComment.getChildComment().add(new_comment);
commentsRepository.save(parentComment);
}
new_comment.setOwnerpost(post);
new_comment.setOwner(user);
commentsRepository.save(new_comment);
}
Comment
#Entity
#Table(name = "comments")
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Comments implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "clientsIdSeq1", sequenceName = "comments_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator="comments_id_seq")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "content")
private String content;
#Column(name = "date_create")
private LocalDate dateCreate;
#Column(name = "count_like")
private Long countLike;
#Column(name = "count_dislike")
private Long counterDislike;
#OneToMany(fetch= FetchType.EAGER,cascade=CascadeType.ALL ,orphanRemoval=true )
#JoinTable(name = "parentchild_comment",
joinColumns = #JoinColumn(name= "parent_id"),
inverseJoinColumns = #JoinColumn(name= "child_id"))
#Fetch(value = FetchMode.SUBSELECT)
private List<Comments> childComment;
#ManyToOne(fetch= FetchType.EAGER,cascade=CascadeType.ALL )
#JoinTable(name = "users_comments",
joinColumns = #JoinColumn(name= "comment_id"),
inverseJoinColumns = #JoinColumn(name= "user_id"))
#JsonIgnoreProperties({"listPost", "listComment"})
private Users owner;
#ManyToOne(fetch= FetchType.EAGER,cascade = {CascadeType.REFRESH })
#JoinTable(name = "posts_comments",
joinColumns = #JoinColumn(name= "post_id"),
inverseJoinColumns = #JoinColumn(name= "comment_id"))
#JsonIgnoreProperties({"listComments"})
private Posts ownerpost;
}
Post
#Entity
#Table(name = "posts")
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Posts implements Serializable {
#Id
#Column(name = "id")
#SequenceGenerator(name = "clientsIdSeq4", sequenceName = "posts_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator ="posts_id_seq" )
private Long id ;
#Column(name = "title")
private String title;
#Column(name = "content")
private String content;
#Column(name = "date_create")
private LocalDate dateCreate;
#Column(name = "count_like")
private Long countLike;
#Column(name = "count_dislike")
private Long counterDislike;
#OneToMany(mappedBy = "ownerpost",fetch= FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true )
#Fetch(value = FetchMode.SUBSELECT)
#JsonIgnoreProperties("childComment")
private List<Comments> listComments;
#ManyToOne(fetch= FetchType.EAGER,cascade=CascadeType.REFRESH)
#JoinTable(name = "users_posts",
joinColumns = #JoinColumn(name= "post_id"),
inverseJoinColumns = #JoinColumn(name= "user_id"))
#JsonIgnoreProperties({"listPost", "listComment"})
private Users owner;
}
User
#Entity
#Table(name = "users")
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "clientsIdSeq5", sequenceName = "users_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "users_id_seq")
private Long id;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#ManyToOne(optional = false, cascade = CascadeType.REFRESH)
#JoinColumn(name = "position_id")
private Position position;
#OneToMany(mappedBy = "owner",fetch= FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true )
#JsonIgnoreProperties("listComments")
#Fetch(value = FetchMode.SUBSELECT)
private List<Posts> listPost;
#OneToMany(mappedBy = "owner",fetch= FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true )
#JsonIgnoreProperties("childComment")
#Fetch(value = FetchMode.SUBSELECT)
private List<Comments> listComment;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Users)) return false;
Users users = (Users) o;
if (!Objects.equals(id, users.id)) return false;
if (!Objects.equals(login, users.login)) return false;
if (!Objects.equals(password, users.password)) return false;
if (!Objects.equals(position, users.position)) return false;
if (!Objects.equals(listPost, users.listPost)) return false;
return Objects.equals(listComment, users.listComment);
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (login != null ? login.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (position != null ? position.hashCode() : 0);
result = 31 * result + (listPost != null ? listPost.hashCode() : 0);
result = 31 * result + (listComment != null ? listComment.hashCode() : 0);
return result;
}
}
my code https://dropmefiles.com/pdv48
Insomnia with with requests https://dropmefiles.com/jPOgB
You mixed the name and the sequence name. The generator attribute must be then name not the sequenceName
#SequenceGenerator(name = "clientsIdSeq1", sequenceName = "comments_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="clientsIdSeq1")

JPA Join query inside #Query

i have three model class there are - User, Menu, Sub-menu.
#Data
#Entity
#Table(name = "CBR_USER")
public class User {
#Id
#GeneratedValue
#Column(name = "CBR_USER_ID")
private Integer cbrUserId;
#Column(name = "LOG_IN_ID")
private String logInId;
private String userId;
private String password;
#Column(name = "FULL_NAME")
private String FULL_NAME;
private String EMAIL;
private String PHONE;
private Integer ROLE_ID;
private String DESIGNATION;
private String branchId;
private Integer IS_VALID;
#ManyToMany(fetch = FetchType.EAGER)
#Fetch(FetchMode.SELECT)
#JoinTable(name = "Conf_menu_Access", joinColumns = #JoinColumn(name = "CBR_USER_ID"), inverseJoinColumns = #JoinColumn(name = "id"))
private List<Menu> menuList;
}
Menu class is :
#Data
#Entity
#Table(name = "CONF_MENU")
public class Menu {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_menu")
#SequenceGenerator(name = "seq_menu", sequenceName = "seq_menu", allocationSize = 1)
private Integer id;
private String name;
private String url;
private Integer accessBy;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "menuId")
private List<SubMenu> menuList;
}
and sub-menu class is
#Data
#Entity
#Table(name = "conf_sub_menu")
public class SubMenu {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_sub_menu")
#SequenceGenerator(name = "seq_sub_menu", sequenceName = "seq_sub_menu", allocationSize = 1)
private Integer id;
#Column(name = "MENU_ID")
private Integer menuId;
private String name;
private String url;
}
after compile my code it's generate another mapping table name as
Conf_menu_Access
this table map user access able menu , it's define in User class.
now i need to implement a sql query which is
SELECT ID ,NAME,
CASE
WHEN (SELECT ID FROM CONF_MENU_ACCESS WHERE CBR_USER_ID = 150 AND ID = CMA.ID )>0 THEN 1
ELSE 0
END AS ACCESSBY
FROM CONF_MENU CMA ORDER BY ID ASC
i want to write this query inside #Query tag, any one can help me how to do this......
You can use
#Query(value = "SELECT ID ,NAME,
CASE
WHEN (SELECT ID FROM CONF_MENU_ACCESS WHERE CBR_USER_ID = 150 AND ID = CMA.ID )>0
THEN 1 ELSE 0
END AS ACCESSBY
FROM CONF_MENU CMA ORDER BY ID ASC", nativeQuery = true)
It might be that column name which you are using it should match the column name in the database and the error which is coming is may be that the column name you are passing is of entity field.

OneToOne in Hibernate causes StackOverflow Exception when calling Mongo save

I have two entities :
Invoice :
#Entity
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Table(name = "invoices")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Invoice implements Serializable {
private static final long serialVersionUID = 1L;
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(columnDefinition = "CHAR(36)")
#Id
private String id;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "invoice")
private InvoiceSequence invoiceSequence;
... // skipped for brevity
InvoiceSequence
#Entity
public class InvoiceSequence {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long seqId;
#OneToOne
#JoinColumn(name = "invoice_id", nullable = false)
#JsonIgnore
private Invoice invoice;
... // skipped for brevity
When calling mongo save,as in :
#Override
public Invoice save(Invoice invoice) {
Invoice savedInv = invoiceRepository.save(invoice);
InvoiceSequence seq = new InvoiceSequence();
seq.setInvoice(savedInv);
InvoiceSequence savedSeq = invoiceSequenceRepository.save(seq);
savedInv.setInvoiceSequence(savedSeq);
return savedInv;
}
i get :
java.lang.StackOverflowError
at java.lang.Class.isInstance(Native Method)
at java.lang.Class.cast(Class.java:3368)
at java.lang.invoke.DirectMethodHandle$Accessor.checkCast(DirectMethodHandle.java:418)
at java.lang.invoke.DirectMethodHandle.checkCast(DirectMethodHandle.java:487)
at com.vulog.billing.domain.Invoice_Accessor_5oixbb.getProperty(Unknown Source)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:432)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:425)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:330)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:425)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:527)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:437)
What am i missing?
Thanks for any help

Delete Operation on Embedded Object Spring JPA

I Have below Entities :
#Entity(name = "USRGRP_MAP")
public class UserGroupMapping {
#Id
#Column(name = "USRGRP_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_GRP_MAP_SEQ")
#SequenceGenerator(sequenceName = "usrgrp_map_seq",allocationSize = 1,name = "USER_GRP_MAP_SEQ")
private Long mappingId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "USR_GRP_ID", referencedColumnName = "USR_GRP_ID")
private UserGroup group;
#Column(name = "USR_USRGRP_ACT")
private String userGroupAct;
getter/setters
}
#Entity(name = "USER")
public class User {
#Id
#Column(name = "USER_ID")
private Long userId;
#Column(name = "LOGIN_ID")
private String userName;
getter/setters
}
#Entity(name = "USR_GRP")
public class UserGroup {
#Id
#Column(name = "USR_GRP_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_GRP_SEQ")
#SequenceGenerator(sequenceName = "usr_grp_seq",allocationSize = 1,name = "USER_GRP_SEQ")
private long groupId;
#Column(name = "GRP_NM")
private String groupName;
#Column(name = "GRP_DESC")
private String groupDesc;
getter/setters
}
UserGroupMapping contains has many to one relationship with both user and group.
Now I want to do CRUD operation on UserGroupMapping for that I have created repository as below:
public interface UserGroupMappingRepository extends JpaRepository<UserGroupMapping, Long> {
List<UserGroupMapping> findByGroup(UserGroup group);
List<UserGroupMapping> findByUser(User user);
}
Now I want to write delete operation(for particular user and group) on UserGroupMapping without deleting any entry in USER and USR_GRP table , Just need to remove entry from USRGRP_MAP table.
I am trying to achieve it using native query:
#Query(value = "delete from USR_USRGRP_MAP where user_id = :userId and usr_grp_id = :groupId",nativeQuery = true)
void deleteUserGroupMappingByUserAndGroup(#Param("userId") Long userId, #Param("groupId") Long groupId);
Facing Exception Invalid SQL grammar, although query work fine in sql developer.
Below is my service class:
#Service
public class UserGroupMappingServiceImpl implements UserGroupMappingService{
#Autowired
private UserGroupMappingRepository repository;
#Override
public void deleteUserGroupMapping(Long userId, Long groupId) {
repository.deleteUserGroupMappingByUserAndGroup(userId,groupId);
}
}
Could anyone suggest correct way to delete entry from UserGroupMapping without deleting user and group ?
Below is USRGRP_MAP table:
USRGRP_ID USER_ID USR_USRGRP_ID USR_USRGRP_ACT
------------- ---------- ------------- -
41 306106 41 Y
14 108527 14 Y
8 295597 8 N
10 296518 10 Y
11 295597 11 Y
Thanks in advance .
Try to change
#Query(value = "delete from USR_USRGRP_MAP where user_id = :userId and usr_grp_id = :groupId",nativeQuery = true)
void deleteUserGroupMappingByUserAndGroup(#Param("userId") Long userId, #Param("groupId") Long groupId);
To this:
#Modifying
#Query(value = "delete from USR_USRGRP_MAP where user_id = :userId and usr_grp_id = :groupId",nativeQuery = true)
void deleteUserGroupMappingByUserAndGroup(#Param("userId") Long userId, #Param("groupId") Long groupId);
Cheers
~Emil

Resources