I have an Entity that has the following multiple fields along with a field that is autogenerated on an insert
Now everytime I want to do an update using merge() in my DAO, it will add a new entry instead of updating the current entry. I am assuming this is due to UPDATE statement on id and the id is new everytime. Is there a way within JPA (Hibernate) to avoid writing a new record in the database and just to an update instead. Please find below my entity object:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "CONFIG_ID")
private long configID;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "CID", referencedColumnName = "CID", insertable = true, updatable = false),
#JoinColumn(name = "DID", referencedColumnName = "DID", insertable = true, updatable = false)})
#Version private <Object> dE;
#Column(name = "HISTORY", nullable = false, unique = false)
private String history;
merge() is supposed for detached entity use . It is an update or insert action depending if the identity of the merged object is null or not. If it is null , a new record will be inserted.
To correctly update an entity in JPA , you simply load it and change its properties. When the transaction commit , JPA will figure out if there are any properties changes and issue the UPDATE SQL if there are changes. No need to call any methods on EntityManager for any update.
Reference:
JPA wiki - Merge
Related
I have 2 entities, one for a taskList and the other for users. The users can create tasks for themselves or their friends, so in the Tasks entity I have different columns referencing the User entity, one for createdBy another for createdFor and a last one for modifiedBy.
In the tasks entity I made the following #ManyToOne JPA relationships:
#ManyToOne(optional = false)
#JoinColumn(name = "created_by", nullable = false,
referencedColumnName = "id")
private User createdBy;
#ManyToOne
#JoinColumn(name = "created_for", nullable = false,
referencedColumnName = "id")
private User createdFor;
#ManyToOne
#JoinColumn(name = "modified_by", nullable = false,
referencedColumnName = "id")
private User modifiedBy;
But I am not sure how to do the linking in the Users side, I had the following #OneToMany relationship:
#OneToMany(mappedBy = "createdFor", cascade = CascadeType.ALL)
private List<Task> taskList;
But I am not sure how to proceed with the other relationships as I don't really need a taskList for the tasks that a user created, or what tasks did he modify.
You do not need to map all things in an Entity , and also do not need to configure every relationships as bi-directional.
So if you really do not need to access the Tasks that are created and modified from a given User , just ignore them and do not map them in User.
It is perfectly valid to configure a relationship as unidirectional. Your existing codes should already work perfectly.
There is already a solution - how to store fk instead the whole entity on many-to-one unidirectional:
#JoinColumn(name = "message_key")
#ManyToOne(targetEntity = Messages.class, fetch = FetchType.LAZY)
private Messages message;
#Column(name = "message_key", insertable = false, updatable = false)
private Long message_fk;
Two questions:
How to do the same for unidirectional many-to-many? Means I have a List<Message> messages and want to keep only List<Long> message_fks.
Will it ensure consistency if I will never use message object? I want to use only message_fk field.
I have problem and i could not find solution. I have bi-directional many to many anotation. I have this tables in DB (mariaDB):
item
section
item_section
Item part:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "item_section",
joinColumns = {
#JoinColumn(name = "item", nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "section", nullable = false, updatable = false)})
private Set<Section> sections;
Section part:
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "sections")
private Set<Item> items;
PROBLEM:
I create some Sections first. Than i want to create Item "within" this section. So i create instance of Item and add some existing Sections into set (Sections i get from DAO). If i persist the Item, records in item and item_section tables are created. But if i get some of affected section from DAO and iterate thru items set database changes are not here, Sections instances are in same state as before insert.
What did i wrong?
PS: i use eclipselink but i do not think it is interesting
SOLUTION
As #Chris said i called persist utimately but one side of direction already exist. No cascades was defined so during persist sections was not persisted nor merge. So best solution for my staless and JTA solution was use merge instead of persist on item and add MERGE cascade to sections collection...
Thanks everybody who came to help but especially to #Chris
It seems you are missing the attribute cascade=CascadeType.ALL. You should try to change
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "item_section",
joinColumns = {
#JoinColumn(name = "item", nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "section", nullable = false, updatable = false)})
private Set<Section> sections;
to
#ManyToMany(cascade=CascadeType.ALL,fetch = FetchType.LAZY)
#JoinTable(name = "item_section",
joinColumns = {
#JoinColumn(name = "item", nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "section", nullable = false, updatable = false)})
private Set<Section> sections;
then it works normally.
I found this mistake via the tutorial Hibernate Many-To-Many Bidirectional Mapping Example
Hope this help
Problem may be in incorrect using of eclipselink's shared cache which is enabled by default. Try to disable it by adding in persistence.xml following:
<shared-cache-mode>NONE</shared-cache-mode>
When you persist or merge Section provider flushes it to database and add Section instance to shared cache so it can be retrieved from memory on next requests. When you persist Item you update only one side of the relationship, but the other side does not get updated, and becomes out of sync. In JPA, as in Java in general, it is the responsibility of the application or the object model to maintain relationships. If your application adds to one side of a relationship, then it must add to the other side. Read more about caching in bi-direction mapping in this article.
I can offer following solutions of your problem:
Disable cache for whole application in persistence.xml
Manually refresh Section in DAO before retrieving it to user
Use cache correctly by updating both sides of relationship:
EDIT: Oops, I guess it would help if I posted the delete code. Sorry.
// delete from permanent store and local list
int index = get_record_to_delete();
if (entityList.getEntities().get(index).getEntityId() != null ) {
// existing persisted record
entityService.delete(entityInfo.getEntities().get(index).getEntityId());
}
//remove from local list
entityList.getEntities().remove(index);
return ownersInfo;
// entityService called by above code
public void delete(MyEntity entity) {
repository.delete(entity); // subclass of org.springframework.data.jpa.repository.JpaRepository
}
I'm using Spring Data/JPA V 1.4.2.RELEASE and hibernate 3.6.10.Final.
I have a situation where I'm deleting an entity, and delete appears to work, but when I refresh my web page, the entity reappears. So, it doesn't produce an error, but the record doesn't get deleted.
This entity has child entities contained within it. I suspect that some kind of entity dependence problem is keeping the entity from being truly "deleted", but I don't know which entity dependency it might be, and Hibernate's not telling me. It doesn't matter if the contained entities get deleted or not, but this entity needs to get deleted. Not sure why that's not happening. Here's the entity I'm trying to delete:
public class MyEntity implements java.io.Serializable {
#Id
#GeneratedValue
#Column(name = "MYID", unique = true, nullable = false)
private Long myId;
#Column(name = "MY_NAME", nullable = false, length = 50)
private String myName;
#ManyToOne
#JoinColumn(name = "ADDRESS_ID", nullable = false)
private AddressEntity myAddress;
#ManyToOne
#JoinColumn(name = "OTHER_ADDRESS_ID", nullable = true)
private AddressEntity myOtherAddress;
#ManyToOne
#JoinColumn(name = "TYPE_CODE", nullable = false)
private MyType myType;
#ManyToOne
#JoinColumn(name = "ADDRESS_CODE", nullable = false)
private AddressCode addressCode;
#Column(name = "OTHER", nullable = false, precision = 3, scale = 0)
private Integer myOther;
#ManyToOne
#JoinColumn(name = "PARENT_ID", nullable = false)
private MyParent parent;
#Column(name = "PHONE", nullable = false, precision = 10, scale = 0)
private Long myPhone;
#Column(name = "SOCIAL", nullable = false, length = 9)
private String mySocial;
}
Anyone see anything that might suggest why this entity won't delete?
This turned out to be a case of a Hibernate #OneToMany annotation canceling the delete.
Because the parent object had a #OneToMany(cascade=CascadeType.ALL) annotation on it, like this:
Class MyParent {
#OneToMany(cascade=CascadeType.ALL)
Set<MyObject> myObjects;
}
Hibernate apparently walks the entire object graph and for some reason, because this parent wasn't being deleted, it canceled the child delete.
The thing to do to detect this is turn your logging all the way up to TRACE and look for the following message from Hibernate:
un-scheduling entity deletion
Those are the magic words that let you know that Hibernate is canceling your delete. This was not particularly well documented, so I hope this helps someone else.
In your mapping class use this cascade type. It will delete the records from database.
#OneToMany(cascade=CascadeType.ALL)
I have the following situation:
I´m trying to build an application which is multi-tenant with the same tenants in one database
with the same tables. As by know Hibernate is not supporting this variant before 5.0 as I found.
I`m trying to solve this by adding a brandId field to every table.
As I build Many To Many relationships I also added this brandId to the ManyToMany Join Table and here (dont know if I can do this, mysql is not complaining) I made a foreign key to both tables while both include the brandid
So now for example I have a table Text(ID,name,brandId) and a Tag(ID,name,brandId) and a join table (text_id,tag_id,brand_id) where the foreign keys are
CONSTRAINT FK_TAG_TEXTS_TAG FOREIGN KEY (TAG_ID,BRAND_ID) REFERENCES TAG (ID,brand),
CONSTRAINT FK_TAG_TEXTS_TEXT FOREIGN KEY (TEXT_ID,BRAND_ID) REFERENCES TEXT (ID,brand)
As you can see Brand ID is used twice.
Then I generated my classes with Hibernate Tools, which created a Composite Primary Key Class as it should and the association in the Tag Class.
#ManyToMany(fetch = FetchType.EAGER,cascade = {CascadeType.PERSIST,CascadeType.MERGE })
#JoinTable(name = "tag_texts", , joinColumns = {
#JoinColumn(name = "TAG_ID", nullable = false, insertable = false, updatable = false),
#JoinColumn(name = "BRAND_ID", nullable = false, insertable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "TEXT_ID", insertable = false, nullable = false, updatable = false),#JoinColumn( name = "BRAND_ID", insertable = false, nullable = false, updatable = false) })
public List<Text> getTexts() {
return this.texts;
}
The problem is now that I get the following exception:
org.hibernate.MappingException: Repeated column in mapping for collection: de.company.domain.Tag.texts column: brand_id
I looked into the Hibernate code in the Collection class which raises the exception.
Here a method 'checkColumnDupliation' is called which uses a Set and inserts the name,
what means that a second time inserting "BRAND_ID" as column leads to this behaviour.
As I found the most common solution for the Repeated column error is by inserting 'insertable = false and updateable = false' when using the same column in several references. This is described here:
Hibernate: Where do insertable = false, updatable = false belong in composite primary key constellations involving foreign keys?
But this seems to be not the same problem as mine.
So my question is: Is there a possibility to fix this with the JPA Annotations and use the Brand ID in both joinColumns and inverseJoinColumns?
The problem is that you want a JoinTable between 3 entities: Text, Tag and Brand.
Probably you will have to use an IdClass, something like :
public class AssociationId implements Serializable {
private long textId;
private long tagId;
private long brandId;
hash and equals function
...
}
Id Class Entity:
#Entity
#Table(name="tag_text_brand")
#IdClass(AssociationId.class)
public class TagTextBrandAssociation {
#Id
private long tagId;
#Id
private long textId;
#Id
private long textId;
#ManyToOne
#PrimaryKeyJoinColumn(name="TAG_ID", referencedColumnName="ID")
private Tag tag;
#ManyToOne
#PrimaryKeyJoinColumn(name="TEXT_ID", referencedColumnName="ID")
private Text text;
#ManyToOne
#PrimaryKeyJoinColumn(name="BRAND_ID", referencedColumnName="ID")
private Brand brand;
...
}
You can use this in your 3 entities like this:
#Entity
public class Text {
#Id
private long id;
...
#OneToMany(mappedBy="text")
private List<TagTextBrandAssociation> tagsAndBrands;
...
}
See here for more information.