The problem that I'm currently facing is the following:
Exception in thread "main" javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session: [de.entities.Genre#28]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1359)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1316)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:881)
at de.model.DatabaseBuilder.importData(DatabaseBuilder.java:87)
at de.main.Main.main(Main.java:55)
So the Exception tells me that I want to insert two different objects with the same primary key id.
All my datas I want to insert into a database using JPA are coming from an XML file. I parse this file with a SAXParser. Of course there are many genre entries with the id 28 because many movies have the same genre.
If I use auto-generated ids, the data wont be correct anymore because all id's are correctly given by the XML file.
How can I solve this problem? Why isn't JPA not just ignoring the fact, that this object is already present in the database and just inserting the ids of the movies and the genres in my m:n table?
Either you are directly calling persist on your Genre instances, or you are calling persist on movie and the movie->Genre mapping has cascade persist. JPA requires providers to throw an exception when persist is called on a detached entity, since persist means you want the provider to insert it, and it is detached because it already exists. So you will get an exception. Sounds like your parser is not able to tell that the Genre entities are the same instance, and so is creating multiple instances of the same data. If this cannot be fixed, you might try using merge instead. Merge will check first if the entity instance is new or detached. If it is new, it will insert, while if it is detached, it will retrieve the data and update it in the database.
Other options would be to make sure you are not calling persist on detached Genre instances. Remove the cascade persist settings on relationships, and ensure that you manually call persist or merge on new Genre instances rather than rely on cascade persist settings.
You just cannot insert two elements with the same identifier. That id must be unique, if not, the database cannot identify the object (oh the irony), and that's why it gives you the exception.
Create a new field called movieId (or whatever you want to name it) and store the id from the xml in that field, but not in the database identifier "id".
You just cannot insert two objects with the same identifier(id).
It throws EntityExistsException,
if the entity already exists.
(If the entity already exists, the EntityExistsException may be thrown when the persist operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time.)
Solution
You can create a new field for movieId, which will store the id of the movie from the XML. but this will create unnecessary redundancy of the data. For the Genre, you should create a new table where you will define movie_genre mapping. You can use one to many mapping for this purpose.
Your mapping of your genres seems to have a wrong relation. From your use case, I think it should be a many-to-many relation.
A valid mapping for your genres should look like:
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.MERGE, CascadeType.REFRESH, })
#JoinTable(name = "MovieGenre", joinColumns = { #JoinColumn(name = "Movie_Id") }, inverseJoinColumns = { #JoinColumn(name = "Genre_Id") })
public Set<Genre> getGenres() {
return this.genres;
}
Related
org.hibernate.HibernateException: identifier of an instance
of org.cometd.hibernate.User altered from 12 to 3
in fact, my user table is really must dynamically change its value, my Java app is multithreaded.
Any ideas how to fix it?
Are you changing the primary key value of a User object somewhere? You shouldn't do that. Check that your mapping for the primary key is correct.
What does your mapping XML file or mapping annotations look like?
You must detach your entity from session before modifying its ID fields
In my case, the PK Field in hbm.xml was of type "integer" but in bean code it was long.
In my case getters and setter names were different from Variable name.
private Long stockId;
public Long getStockID() {
return stockId;
}
public void setStockID(Long stockID) {
this.stockId = stockID;
}
where it should be
public Long getStockId() {
return stockId;
}
public void setStockId(Long stockID) {
this.stockId = stockID;
}
In my case, I solved it changing the #Id field type from long to Long.
In my particular case, this was caused by a method in my service implementation that needed the spring #Transactional(readOnly = true) annotation. Once I added that, the issue was resolved. Unusual though, it was just a select statement.
Make sure you aren't trying to use the same User object more than once while changing the ID. In other words, if you were doing something in a batch type operation:
User user = new User(); // Using the same one over and over, won't work
List<Customer> customers = fetchCustomersFromSomeService();
for(Customer customer : customers) {
// User user = new User(); <-- This would work, you get a new one each time
user.setId(customer.getId());
user.setName(customer.getName());
saveUserToDB(user);
}
In my case, a template had a typo so instead of checking for equivalency (==) it was using an assignment equals (=).
So I changed the template logic from:
if (user1.id = user2.id) ...
to
if (user1.id == user2.id) ...
and now everything is fine. So, check your views as well!
It is a problem in your update method. Just instance new User before you save changes and you will be fine. If you use mapping between DTO and Entity class, than do this before mapping.
I had this error also. I had User Object, trying to change his Location, Location was FK in User table. I solved this problem with
#Transactional
public void update(User input) throws Exception {
User userDB = userRepository.findById(input.getUserId()).orElse(null);
userDB.setLocation(new Location());
userMapper.updateEntityFromDto(input, userDB);
User user= userRepository.save(userDB);
}
Also ran into this error message, but the root cause was of a different flavor from those referenced in the other answers here.
Generic answer:
Make sure that once hibernate loads an entity, no code changes the primary key value in that object in any way. When hibernate flushes all changes back to the database, it throws this exception because the primary key changed. If you don't do it explicitly, look for places where this may happen unintentionally, perhaps on related entities that only have LAZY loading configured.
In my case, I am using a mapping framework (MapStruct) to update an entity. In the process, also other referenced entities were being updates as mapping frameworks tend to do that by default. I was later replacing the original entity with new one (in DB terms, changed the value of the foreign key to reference a different row in the related table), the primary key of the previously-referenced entity was already updated, and hibernate attempted to persist this update on flush.
I was facing this issue, too.
The target table is a relation table, wiring two IDs from different tables. I have a UNIQUE constraint on the value combination, replacing the PK.
When updating one of the values of a tuple, this error occured.
This is how the table looks like (MySQL):
CREATE TABLE my_relation_table (
mrt_left_id BIGINT NOT NULL,
mrt_right_id BIGINT NOT NULL,
UNIQUE KEY uix_my_relation_table (mrt_left_id, mrt_right_id),
FOREIGN KEY (mrt_left_id)
REFERENCES left_table(lef_id),
FOREIGN KEY (mrt_right_id)
REFERENCES right_table(rig_id)
);
The Entity class for the RelationWithUnique entity looks basically like this:
#Entity
#IdClass(RelationWithUnique.class)
#Table(name = "my_relation_table")
public class RelationWithUnique implements Serializable {
...
#Id
#ManyToOne
#JoinColumn(name = "mrt_left_id", referencedColumnName = "left_table.lef_id")
private LeftTableEntity leftId;
#Id
#ManyToOne
#JoinColumn(name = "mrt_right_id", referencedColumnName = "right_table.rig_id")
private RightTableEntity rightId;
...
I fixed it by
// usually, we need to detach the object as we are updating the PK
// (rightId being part of the UNIQUE constraint) => PK
// but this would produce a duplicate entry,
// therefore, we simply delete the old tuple and add the new one
final RelationWithUnique newRelation = new RelationWithUnique();
newRelation.setLeftId(oldRelation.getLeftId());
newRelation.setRightId(rightId); // here, the value is updated actually
entityManager.remove(oldRelation);
entityManager.persist(newRelation);
Thanks a lot for the hint of the PK, I just missed it.
Problem can be also in different types of object's PK ("User" in your case) and type you ask hibernate to get session.get(type, id);.
In my case error was identifier of an instance of <skipped> was altered from 16 to 32.
Object's PK type was Integer, hibernate was asked for Long type.
In my case it was because the property was long on object but int in the mapping xml, this exception should be clearer
If you are using Spring MVC or Spring Boot try to avoid:
#ModelAttribute("user") in one controoler, and in other controller
model.addAttribute("user", userRepository.findOne(someId);
This situation can produce such error.
This is an old question, but I'm going to add the fix for my particular issue (Spring Boot, JPA using Hibernate, SQL Server 2014) since it doesn't exactly match the other answers included here:
I had a foreign key, e.g. my_id = '12345', but the value in the referenced column was my_id = '12345 '. It had an extra space at the end which hibernate didn't like. I removed the space, fixed the part of my code that was allowing this extra space, and everything works fine.
Faced the same Issue.
I had an assosciation between 2 beans. In bean A I had defined the variable type as Integer and in bean B I had defined the same variable as Long.
I changed both of them to Integer. This solved my issue.
I solve this by instancing a new instance of depending Object. For an example
instanceA.setInstanceB(new InstanceB());
instanceA.setInstanceB(YOUR NEW VALUE);
In my case I had a primary key in the database that had an accent, but in other table its foreign key didn't have. For some reason, MySQL allowed this.
It looks like you have changed identifier of an instance
of org.cometd.hibernate.User object menaged by JPA entity context.
In this case create the new User entity object with appropriate id. And set it instead of the original User object.
Did you using multiple Transaction managers from the same service class.
Like, if your project has two or more transaction configurations.
If true,
then at first separate them.
I got the issue when i tried fetching an existing DB entity, modified few fields and executed
session.save(entity)
instead of
session.merge(entity)
Since it is existing in the DB, when we should merge() instead of save()
you may be modified primary key of fetched entity and then trying to save with a same transaction to create new record from existing.
I'm newbie in Hibernate and I'm trying to learn about JPA and Hibernate.
I want to know that what is the reason that Hibernate does not allow to save the object which references an unsaved transient instance? I want to know WHY this is a problem?
I asked someone and some of them answer me like this:
How could we possibly map the customer to the address, if there is no
adress record in the DB yet?
and
you are assigning particular Address to Customer. But Address does
not have any ID
but honestly I can't understand them.
(I know that an exception will be thrown and the solution is Cascade but I want to the reason of the problem inside the database)
now, let's assume we have all of these code:
(I use Bidirectional One-To-One relationship for my example)
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#OneToOne(mappedBy = "customer")
private Address address;
}
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String zipCode;
#OneToOne
private Customer customer;
}
public static void main(String[] args) {
EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin(); // Begin Transaction
Customer c1 = new Customer("Mi", "S");
Address addrss1 = new Address("5412 S 5th", "212524");
c1.setAddress(addrss1);
addrss1.setCustomer(c1);
entityManager.persist(c1);
entityManager.getTransaction().commit(); // Commit
entityManager.close();
}
and let's assume that the exception is not thrown and java and hibernate have allowed us to run our code and this is our customer table.
id firstName lastName
---------------------------------
1 Mi S
and this is our address table:
id street zipCode customer_id
---------------------------------------------
- - - -
now, what is the problem? everything in these Bidirectional One-To-One relationship seems right.
then what is the problem?
PS:
if it is possible, please explain and show me code.
I can understand better with code. thank you.
I want to see for example if we are allowed to save the object which references an unsaved transient instance, what problems will we face in our code and in our tables (for example do we have any problem when we want to retrieve a customer and etc)
Because your adress entity have the primay key of customer as a foreign key ,(since mappedby is in Customer entity) ,and the customer referenced by the adress has no id ,which tells hibernate that that entity was never persisted in the database (which literally means transient) ,and hibernate needs a persisted/managed entity to make sure it exists in the database so that the adress object can be associated with an existing customer.
Customer is new, and it is clear from the persist call you want to insert it, but it isn't clear what you want to happen to any of customer's references. To make it clear, you define what you want the JPA provider (Hibernate) to do in the mappings under any/all circumstances - this is what the cascade operations refer to. In this case, JPA will look at the customer.address OneToOne mapping and find nothing defined; Address is NOT managed in this EntityManager context, so it doesn't know what to do to handle this relationship, so it signals you've made a mistake by throwing an error.
If it let it through, your Customer instance references something that does not exist, and its state does not match what is in the database. What you pass into persist should be what you would get back on reads, so it should reflect the state that is in the database.
The issue isn't directly with your persist call, as the spec does allow providers to ignore references to detached/new instances that don't have cascade settings - what happens is just undefined. Where you go wrong in this situation is on flush/commit, which is when the persistence unit is synchronized to the database (section 3.2.4 of JPA 3.0), which requires providers to go through managed entities and then determine any changes. Adding a new address pre persist will result in the same issue as if you did it post persist, and requires providers to throw an IllegalStateException if it discovers new or removed entities and rollback the transaction.
Why this is a problem: JPA is very big on entity Identity, as this enables caching of these entities in multiple levels of caches, and this entity might go into those caches as it is. It has to know what to do with references to entities that do not exist, and the spec decided to require an exception. Even to your app this is and should be a problem, as the EntityManager context is a unit of work, and the state within that unit of work is based on something that is wrong. Your Customer doesn't really have an address when this is said and done, yet your application business logic thinks it assigned one, with state that just isn't going to be there afterward.
You already know the solutions:
correct the customer to have a valid, managed address by calling persist on it directly in this same EntityManager context.
set the cascade options on the mapping to cascade persist to address for you
don't set addresses on a new customer in the same operation.
I have an old database where there are two tables with implicit association between them:
booking
- id
- name
... some other fields...
and
booking_info
- id
- booking_id
... some other fields...
Due to the current database design there no any constraints between these two tables, which means that one booking entry may exist without any booking_info entries and vice versa. booking_id in booking_info table is an implicit foreign key which refers to booking table (column id), but it also may refer to the absent booking.
I have created the following JPA mapping for these tables:
#Entity
public class Booking {
#Id
private Long id;
private String name;
// getters & setters
}
and
#Entity
public class BookingInfo {
#Id
private Long id;
#OneToOne
#JoinColumn(name = "booking_id")
private Booking booking
// getters & setters
}
Now I need to be able to persist a BookingInfo entity even if there's no related Booking entry in the database.
BookingInfo bookingInfo = new BookingInfo();
bookingInfo.setId(1);
Booking booking = new Booking();
booking.setId(182); // let's say that there's no booking with id 182 in my database, but I still need to persist this bookingInfo
bookingInfo.setBooking(booking);
bookingInfoRepository.save(bookingInfo); // using Spring Data JPA
If I run this code then I get javax.persistence.EntityNotFoundException since booking with id 182 is absent.
What would be the proper workaround for my case using JPA or Hibernate.
Btw, I also tried to use Hibernate's #NotFound annotation. As a result, save method doesn't throw javax.persistence.EntityNotFoundException and entity gets persisted int the database, but the problem is that booking_id in the database always null.
Any help would be appreciated.
I am not sure my answer will help you or not, but the result you are getting perfectly make sense. As you are setting a JPA object, and that object is not present, hence the null value is saved.
If you want to save 182 as an integer, you don't do JPA relationship. Instead, you just use booking-id as an integer field in booking-info. And that makes sense because you actually do not have the relationship between those tables which the JPA is trying to achieve.
But I am sure you just want to save 182 and as well as maintain the JPA relationship. And I am sure you already know it, but DB integrity is not being maintained with the approach you are taking. I am sure there is enough reason behind that. But my recommendation would be applying proper constraints in the DB and then in JPA.
I have 2 classes: Driver and Car. Cars table updated in separate process. What I need is to have property in Driver that allows me to read full car description and write only Id pointing to existing Car. Here is example:
#Entity(name = "DRIVER")
public class Driver {
... ID and other properties for Driver goes here .....
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "CAR_ID")
private Car car;
#JsonView({Views.Full.class})
public Car getCar() {
return car;
}
#JsonView({Views.Short.class})
public long getCarId() {
return car.getId();
}
public void setCarId(long carId) {
this.car = new Car (carId);
}
}
Car object is just typical JPA object with no back reference to the Driver.
So what I was trying to achieve by this is:
I can read full Car description using detailed JSON View
or I can read only Id of the Car in Short JsonView
and most important, when creating new Driver I just want to pass in JSON ID of the car.
This way I dont need to do unnesessery reads for the Car during persist but just update Id.
Im getting following error:
object references an unsaved transient instance - save the transient instance before flushing : com.Driver.car -> com.Car
I dont want to update instance of the Car in DB but rather just reference to it from Driver. Any idea how to achieve what I want?
Thank you.
UPDATE:
Forgot to mention that the ID of the Car that I pass during creation of the Driver is valid Id of the existing Car in DB.
You can do this via getReference call in EntityManager:
EntityManager em = ...;
Car car = em.getReference(Car.class, carId);
Driver driver = ...;
driver.setCar(car);
em.persist(driver);
This will not execute SELECT statement from the database.
As an answer to okutane, please see snippet:
#JoinColumn(name = "car_id", insertable = false, updatable = false)
#ManyToOne(targetEntity = Car.class, fetch = FetchType.EAGER)
private Car car;
#Column(name = "car_id")
private Long carId;
So what happens here is that when you want to do an insert/update, you only populate the carId field and perform the insert/update. Since the car field is non-insertable and non-updatable Hibernate will not complain about this and since in your database model you would only populate your car_id as a foreign key anyway this is enough at this point (and your foreign key relationship on the database will ensure your data integrity). Now when you fetch your entity the car field will be populated by Hibernate giving you the flexibility where only your parent gets fetched when it needs to.
You can work only with the car ID like this:
#JoinColumn(name = "car")
#ManyToOne(targetEntity = Car.class, fetch = FetchType.LAZY)
#NotNull(message = "Car not set")
#JsonIgnore
private Car car;
#Column(name = "car", insertable = false, updatable = false)
private Long carId;
That error message means that you have have a transient instance in your object graph that is not explicitly persisted. Short recap of the statuses an object can have in JPA:
Transient: A new object that has not yet been stored in the database (and is thus unknown to the entitymanager.) Does not have an id set.
Managed: An object that the entitymanager keeps track of. Managed objects are what you work with within the scope of a transaction, and all changes done to a managed object will automatically be stored once the transaction is commited.
Detached: A previously managed object that is still reachable after the transction commits. (A managed object outside a transaction.) Has an id set.
What the error message is telling you is that the (managed/detached) Driver-object you are working with holds a reference to a Car-object that is unknown to Hibernate (it is transient). In order to make Hibernate understand that any unsaved instances of Car being referenced from a Driver about be saved should also be saved you can call the persist-method of the EntityManager.
Alternatively, you can add a cascade on persist (I think, just from the top of my head, haven't tested it), which will execute a persist on the Car prior to persisting the Driver.
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
#JoinColumn(name = "CAR_ID")
private Car car;
If you use the merge-method of the entitymanager to store the Driver, you should add CascadeType.MERGE instead, or both:
#ManyToOne(fetch=FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "CAR_ID")
private Car car;
public void setCarId(long carId) {
this.car = new Car (carId);
}
It is actually not saved version of a car. So it is a transient object because it hasn't id. JPA demands that you should take care about relations. If entity is new (doesn't managed by context) it should be saved before it can relate with other managed/detached objects (actually the MASTER entity can maintain it's children by using cascades).
Two ways: cascades or save&retrieval from db.
Also you should avoid set entity ID by hand. If you do not want to update/persist car by it's MASTER entity, you should get the CAR from database and maintain your driver with it's instance. So, if you do that, Car will be detached from persistence context, BUT still it will have and ID and can be related with any Entity without affects.
Add optional field equal false like following
#ManyToOne(optional = false) // Telling hibernate trust me (As a trusted developer in this project) when building the query that the id provided to this entity is exists in database thus build the insert/update query right away without pre-checks
private Car car;
That way you can set just car's id as
driver.setCar(new Car(1));
and then persist driver normal
driverRepo.save(driver);
You will see that car with id 1 is assigned perfectly to driver in database
Description:
So what make this tiny optional=false makes may be this would help more https://stackoverflow.com/a/17987718
Here's the missing article that Adi Sutanto linked.
Item 11: Populating a Child-Side Parent Association Via Proxy
Executing more SQL statements than needed is always a performance penalty. It is important to strive to reduce their number as much as possible, and relying on references is one of the easy to use optimization.
Description: A Hibernate proxy can be useful when a child entity can be persisted with a reference to its parent ( #ManyToOne or #OneToOne lazy association). In such cases, fetching the parent entity from the database (execute the SELECT statement) is a performance penalty and a pointless action. Hibernate can set the underlying foreign key value for an uninitialized proxy.
Key points:
Rely on EntityManager#getReference() In Spring
use JpaRepository#getOne() Used in this example,
in Hibernate, use load()
Assume two entities, Author and Book, involved in a unidirectional #ManyToOne association (Author is the parent-side) We fetch the author via a proxy (this will not trigger a SELECT), we create a new book
we set the proxy as the author for this book and we save the book (this will trigger an INSERT in the book table)
Output sample:
The console output will reveal that only an INSERT is triggered, and no SELECT
Source code can be found here.
If you want to see the whole article put https://dzone.com/articles/50-best-performance-practices-for-hibernate-5-amp into the wayback machine. I'm not finding a live version of the article.
PS. I'm currently on a way to handle this well when using Jackson object mapper to deserialize Entities from the frontend. If you're interested in how that plays into all this leave a comment.
Use cascade in manytoone annotation
#manytoone(cascade=CascadeType.Remove)
I have a Person entity with an embeddable Address and there's a one-to-many relation between them (a person can have multiple addresses). The current mapping is something like this:
#Embeddable
public class Address {
// ... attributes
}
#Entity
public class Person {
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid")
)
/*
Attribute ovverrides with annotations
*/
private java.util.Set<Address> addresses = new java.util.HashSet<Address>();
}
Using this annotation means that in the database I have a Person_addresses table which contains all the address attributes and a personid. But it also means that if I have a person with an address list and I update the address list, Hibernate deletes all the related records and inserts them (the modified ones) again.
As far as I know there's a way to have a primary key in this table for each record - in this case hibernate can decide which item of the list needs to be updated. So my question is, how can I map an embeddable list with identifiers in the joining table? (I hope it's understandable what I want:)).
http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#Primary_keys_in_CollectionTable
The JPA 2.0 specification does not provide a way to define the Id in
the Embeddable. However, to delete or update a element of the
ElementCollection mapping, some unique key is normally required.
Otherwise, on every update the JPA provider would need to delete
everything from the CollectionTable for the Entity, and then insert
the values back. So, the JPA provider will most likely assume that the
combination of all of the fields in the Embeddable are unique, in
combination with the foreign key (JoinColumn(s)). This however could
be inefficient, or just not feasible if the Embeddable is big, or
complex. Some JPA providers may allow the Id to be specified in the
Embeddable, to resolve this issue. Note in this case the Id only needs
to be unique for the collection, not the table, as the foreign key is
included. Some may also allow the unique option on the CollectionTable
to be used for this. Otherwise, if your Embeddable is complex, you may
consider making it an Entity and use a OneToMany instead.
So thats it, it can't be done.
As maestro's reply implies, the only portable solution is to convert this to use an entity and a one-to-many.
That said, Hibernate has a non-spec feature called an "id bag" which allows you to map a basic or embeddable collection with an identifier for each row, thereby giving you the efficient updates you want:
#Entity
public class Person {
#CollectionId( columns={"address_id"}, type="int", generator="increment" )
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid"))
private java.util.List<Address> addresses = new java.util.ArrayList<Address>();
}
Notice the switch from Set to List however. Also notice the generated table structure... looks an awful lot like an entity ;)