I have 3 classes:
Record/ Profile / Options
#Entity
#Table(name="Record")
public class Record implements Serializable {
#Id
#GeneratedValue
private int id;
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="ProfileId")
private Profile profile;
....
}
#Entity
#Table(name="Profile")
public class Profile implements Serializable {
#Id
#GeneratedValue
private int id;
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="OptionId")
private Option option;
....
}
#Entity
#Table(name="Option")
public class Option implements Serializable {
#Id
#GeneratedValue
private int id;
private String name;
....
}
let's say the original option is "50M" and then I change the record1.profile1.option to "10M"
Also when I do record1.setId(null);recordRepository.save(record1);
I want to create an new entry from record1(as a change history).
In this case because the option is nested, the cascade type of merge will not persist the changes happened in profile. Thus when I get the record back, it will still say that recordNew.profile1.option is 50M
But if I change the cascadeType to CascadeType.ALL or CascadeType.PERSISTin the Record class, when I try to save the new entry, it seems Spring Data JPA will complains about detached property as: org.hibernate.PersistentObjectException: detached entity passed to persist: com.test.lalala.profile.Profile
Is there a way that I could fix this?
Related
I am using Hibernate 6. I have a column defined in a postgres database as auuid.
However, I removed the type annotation for a postgres uuid because the type was removed from Hibernate 6. Now I get the following error:
ERROR: column "id" is of type uuid but expression is of type bytea
#Entity
public class SomeObject implements Serializable {
#EmbeddedId
#Access(AccessType.PROPERTY)
private SomeKey id;
}
#Embeddable
public class SomeKey implements Serializable {
private UUID id;
private int other;
}
SomeObject obj= new SomeObject();
obj.setId(new SomeKey (UUID.randomUUID(),0));
session.persist(obj);
Not sure how to fix. It was working before with #Type annotation.
Edit:
Seems like this also causes the same issue
#Basic
#JdbcTypeCode(SqlTypes.UUID)
private UUID id;
This is an incredibly hacky solution, but I was able to get it to work by creating a new PostgresUUIDType which mirrored the one from Hibernate 5.6.*.
That would get reads to work, but not writes. To fix writes, I had to also remove #Access(AccessType.PROPERTY).
So final working code looks like this:
#Entity
public class SomeObject implements Serializable {
#EmbeddedId
private SomeKey id;
}
#Embeddable
public class SomeKey implements Serializable {
#Type(value=PostgresUUIDType.class)
private UUID id;
private int other;
}
I have a logic that saves some data and I use spring boot + spring data jpa.
Now, I have to save one object, and after moment, I have to save another objeect.
those of object consists of three primary key properties.
- partCode, setCode, itemCode.
let's say first object has a toString() returning below:
SetItem(partCode=10-001, setCode=04, itemCode=01-0021, qty=1.0, sortNo=2, item=null)
and the second object has a toString returning below:
SetItem(partCode=10-001, setCode=04, itemCode=01-0031, qty=1.0, sortNo=2, item=null)
there is a difference on itemCode value, and itemCode property is belonged to primary key, so the two objects are different each other.
but in my case, when I run the program, the webapp saves first object, and updates first object with second object value, not saving objects seperately.
(above image contains different values from this post question)
Here is my entity information:
/**
* The persistent class for the set_item database table.
*
*/
#Data
#DynamicInsert
#DynamicUpdate
#Entity
#ToString(includeFieldNames=true)
#Table(name="set_item")
#IdClass(SetGroupId.class)
public class SetItem extends BasicJpaModel<SetItemId> {
private static final long serialVersionUID = 1L;
#Id
#Column(name="PART_CODE")
private String partCode;
#Id
#Column(name="SET_CODE")
private String setCode;
#Id
#Column(name="ITEM_CODE")
private String itemCode;
private Double qty;
#Column(name="SORT_NO")
private int sortNo;
#Override
public SetItemId getId() {
if(BooleanUtils.ifNull(partCode, setCode, itemCode)){
return null;
}
return SetItemId.of(partCode, setCode, itemCode);
}
#ManyToMany(fetch=FetchType.LAZY)
#JoinColumns(value = {
#JoinColumn(name="PART_CODE", referencedColumnName="PART_CODE", insertable=false, updatable=false)
, #JoinColumn(name="ITEM_CODE", referencedColumnName="ITEM_CODE", insertable=false, updatable=false)
})
private List<Item> item;
}
So the question is,
how do I save objects separately which the objects' composite primary keys are partially same amongst them.
EDIT:
The entity extends below class:
#Setter
#Getter
#MappedSuperclass
#DynamicInsert
#DynamicUpdate
public abstract class BasicJpaModel<PK extends Serializable> implements Persistable<PK>, Serializable {
#Override
#JsonIgnore
public boolean isNew() {
return null == getId();
}
}
EDIT again: embeddable class.
after soneone points out embeddable class, I noticed there are only just two properties, it should be three of it. thank you.
#Data
#NoArgsConstructor
#RequiredArgsConstructor(staticName="of")
#Embeddable
public class SetGroupId implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#NonNull
private String partCode;
#NonNull
private String setCode;
}
Check howto use #EmbeddedId & #Embeddable (update you might need to use AttributeOverrides in id field, not sure if Columns in #Embeddable works).
You could create class annotated #Embeddable and add all those three ID fields there.
#Embeddable
public class MyId {
private String partCode;
private String setCode;
private String itemCode;
}
Add needed getters & setters.
Then set in class SetItem this class to be the id like `#EmbeddedId´.
public class SetItem {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="partCode",
column=#Column(name="PART_CODE")),
#AttributeOverride(name="setCode",
column=#Column(name="SET_CODE"))
#AttributeOverride(name="itemCode",
column=#Column(name="ITEM_CODE"))
})
MyId id;
Check also Which annotation should I use: #IdClass or #EmbeddedId
Be sure to implement equals and hashCode in SetGroupId.
Can you provide that class?
I'm working with Spring App, so to work with DB I use Spring Data JPA. Firstly I saved an object. And after some time I need to update this object in the table. But at this moment my object contains one field which is null. But I don't want to update this field with null. So my question is how to prevent updating fields with null? Maybe there is an annotation or some property to solve my problem.My entity:
#Entity
#Table(name = "users")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long id;
#NotNull
#Column(name = "user_name")
#Field
private String username;
#NotNull
#Column(name = "user_identity")
private String identity;
#Column(name="user_image")
private String image;
#Column(name="user_joined")
private String date;
#Column(name="user_origin")
private String origin;
#Column(name="user_sub")
private String sub;
I save and update this entity with implementation of JpaRepository:
#Repository
public interface UserRepository extends JpaRepository<User, Long>
it looks like this:
#Autowired
private UserRepository userRepository;
....
userRepository.save(user);
I've saved my object with not null sub-field. And now I want to update some fields of saved entity, but not sub field, which is null in current object. I wonder if there is any possibility to avoid changing user_sub field to null?
You can add #DynamicUpdate annotation to your User class. This will ignore the fields whose values are null. You can simply do like:
//other annotations
#DynamicUpdate
public class User {
// other codes inside class
}
You can follow a good example from Mkyong's site.
Thanks, guys. I found the solution: #Query will help to update fields that I need
I have two pojo classes with one-to-many relationship in hibernate
CustomerAccountEnduserOrderDetails.class
#Entity #Table(name="customer_account_enduser_order_details")
public class CustomerAccountEnduserOrderDetails implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Long id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "product_id", insertable = false, updatable = false)
private CustomerCmsProduct customerCmsProduct;
}
Second is CustomerCmsProduct.class
#Entity
#Table(name="customer_cms_product")
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomerCmsProduct {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private Long id;
#Column(name="name")
private String name;
#Column(name="offer_price")
private String offerPrice;
#Column(name="original_price")
private String originalPrice;
#Column(name="discount")
private String discount;
}
Here if I fetch the object of CustomerAccountEnduserOrderDetails class,then i will get the CustomerCmsProduct class also , my problem is that here i want the specific column of CustomerCmsProduct table (not all by default i am getting all) like only id and originalPrice.
How i can do like that projection here?
In the service layer or at a webservice layer( if this is a web project) Create two different classes other than #Entity as DTO(Data Transfer Objects) which helps is data transfer from one layer to the other.
public class CustomerAccountEnduserOrderDetailsPojo {
private List<CustomerCmsProductPojo> productPojoList = new ArrayList<> ();
// getter and setter
}
public class CustomerCmsProductPojo {}
Follow the below steps
Retrieve the #Entity class data by executing the query from service layer.
Iterate over all the fields and copy only required fields to pojo layer
Expose the data to other layers using service method.
This way, we can avoid changing the custom hibernate behavior as it is linked with many parameters like cache, one to many queries that are fired per iteration.
And also, do any customization that you want in this layer. Hope this is multi layered project where you have different layers which servers different purpose.
Hi I am new to Spring Data JPA and I am wondering even though I pass the Id to the entity, the Spring data jpa is inserting instead of merge. I thought when I implement the Persistable interface and implement the two methods:
public Long getId();
public Boolean isNew();
It will automatically merge instead of persist.
I have an entity class called User like:
#Entity
#Table(name = "T_USER")
public class User implements Serializable, Persistable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID")
private Long id;
#Column(name = "CREATION_TIME", nullable = false)
private Date creationTime;
#Column(name = "FIRST_NAME", nullable = false)
private String firstName;
#Column(name = "LAST_NAME", nullable = false)
private String lastName;
#Column(name = "MODIFICATION_TIME", nullable = false)
private Date modificationTime;
And have another class
#Entity
#Table(name = "T_USER_ROLE")
public class UserRole implements Serializable, Persistable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long roleId;
#Column(name = "ROLE_NAME")
private String userRole;
}
I have a custom repository called UserRepostory extending JpaReopistory. I am hitting the save for merge and persist as I see the implementation demonstrate that Spring Data Jpa uses above two methods to either update or insert.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
I have been trying to figure out but didn't get any clue. Maybe you
guys can help.
I ran into this issue, tried to implement Persistable to no avail, and then looked into the Spring Data JPA source. I don't necessarily see this in your example code, but I have a #Version field in my entity. If there is a #Version field Spring Data will test that value to determine if the entity is new or not. If the #Version field is not a primitive and is null then the entity is considered new.
This threw me for a long time in my tests because I was not setting the version field in my representation but only on the persisted entity. I also don't see this documented in the otherwise helpful Spring Data docs (which is another issue...).
Hope that helps someone!
By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is null, then the entity will be assumed as new, otherwise as not new. It's Id-Property inspection Reference
If you are using Spring JPA with EntityManager calling .merge() will update your entity and .persist() will insert.
#PersistenceContext
private EntityManager em;
#Override
#Transactional
public User save(User user) {
if (user.getId() == null) {
em.persist(user);
return user;
} else {
return em.merge(user);
}
}
There is no need to implement the Persistable interface.