Stop Hibernate from updating existing records

I am trying to get hibernation to write new objects every time I do a save, rather than updating existing records.

public class Risk { private long riskID; private Vehicle vehicle; } public class Vehicle { private long vehicleID; private long riskID; private String regNumber; private String abiCode; private String make; private String model; } 

So, if you write a risk for the database. Then I change the car on the Internet and try to keep the risk for the database a second time. I would like to have two risks in the risk table and two cars in the car table.

I am currently using a hibernate Save (Object o) session. This always creates a new risk in the database, but never creates a new car. It just updates the original.

Here is my mapping file:

 <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class lazy="false" name="uk.co.test.Risk" schema="quote_engine" table="GV_RISK" > <id column="riskID" name="riskID" type="long"> <generator class="identity"/> </id> <many-to-one name="vehicle" class="uk.co.test.Vehicle" column="vehicleID" not-null="true" cascade="all" unique="true" /> </class> 

 <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="uk.co.test.Vehicle" schema="quote_engine" table="GV_VEHICLE"> <id name="vehicleID" type="long" column="vehicleID"> <generator class="identity" /> </id> <property name="regNumber" type="string" column="regNumber" /> <property name="abiCode" type="string" column="abiCode" /> <property name="make" type="string" column="make" /> <property name="model" type="string" column="model" /> </class> 

+4
source share
12 answers

I think an easier way to do this:

  • Saving risk and vehicle in one session.
  • Once you close the session, set the identifiers to null.
  • Then, if necessary, re-save the objects.

It is very important to close the Hibernate session (use a separate session for each save) so that the objects are disconnected, and when the objects become attached, Hibernates thinks they are completely new.

Note: you must change Long for a long time (wrapper primitive)

+4
source

I really doubt that you can make Hibernate so automatically. You want to redefine one of the most basic ORM behaviors. Their bag is that they track your objects in some session in order to save these updates in the database without having to sort these updates yourself.

If you do not want to update records, make your setters private and annotate your fields to gain access to the fields. In xml, set the default-access="field" property in the <hibernate-mapping> .

Then you need to create a new object before adding it to your session. You have a service with a method that looks something like this:

 public void saveVehicle(Vehicle vehicle) { Vehicle vehicleNew = vehicle.copy(); // Risk would be copied via risk.copy() in vehicle.copy() vehicleDao.save(vehicleNew) } 
+5
source

Both Risk instances are likely to have the same vehicle instance in their many-to-one relationship. If you change the Vehicle instance, you change it for both Risk instances instead of creating a new instance.

You either need to fork out the vehicle instance, or you need to save the clone instance.

I give an idea (not working code) for two possibilities. Assume that the instances of Risk are r1 and r2.

1) With eviction:

 Risk r2 = r1.clone(); // or whatever you do to create your second risk instance session.evict(r2.getVehicle()); session.save(r2.getVehicle()); // session.save(r2) also should work. 

(note: in this case, r1 also refers to the instance of vehicle r2. If you no longer use the instance of r1, then it works. If you still want to use the instance of r1, you must evict it and reload it from the database.)

2) With the clone:

 Risk r2 = r1.clone(); // or whatever you do to create your second risk instance Vehicle v2 = r1.getVehicle.clone(); // create a new instance! r2.setVehicle(v2); session.save(r2); 
+3
source

I assume that one vehicle may have many risks?

the risk class and comparison with risk hibernation look fine, but what about the vehicle?

The class should be:

 public class Vehicle { private long vehicleID; private Set<Risk> risks; private String regNumber; private String abiCode; private String make; private String model; } 

See the set of risks (based on the many risks associated with one attitude to the vehicle). Also, in your vehicle you should have something like

 <set name="risks" table="GV_RISK"> <key column="riskID" /> <one-to-many class="Risk"/> </set> 

Hope this helps and good luck :)

+2
source

This will help to see the DDL for each table. You use a many-to-one mapping for a one-to-one association using a unique foreign key association. If they share the same primary key, you can use one to one.

You can try to make a one-on-one bi-directional display in your vehicle mapping, but this is not necessary. This is how it would look.

 <one-to-one name="risk" class="uk.co.test.Risk" property-ref="riskID"/> 

However, what you are trying to do is largely contested. I could modify the class definitions a bit and double check your DDL.

For example (keeping the hbm mapping exactly the same as you defined above):

The structure of the table is as follows:

 create table GV_VEHICLE ( vehicleID int(11) not null auto_increment, regNumber varchar(50) not null, abiCode varchar(50) not null, make varchar(50) not null, model varchar(50) not null, primary key(vehicleID))); create table GV_RISK ( riskID int(11) not null auto_increment, vehicleID int(11), primary key(riskID), foreign key(vehicleID) references GV_VEHICLE(vehicleID)); 

Create definitions from one to one class, each with confirmation to each other

 public class Vehicle { private long vehicleID; private String regNumber; private String abiCode; private String make; private String model; private Risk risk; } public class Risk { private long riskID; private Vehicle vehicle; } 
+2
source

I'm going to assume that the code that runs in your case looks something like this:

 vehicle = new Vehicle(make, model, regNumber, abiCode) risk = new Risk(vehicle); session.save(risk); vehicle.setMake(otherMake) risk = new Risk(vehicle) session.save(risk); 

The problem that you encounter is that as soon as you call the first session.save (), the change state of the Risk and Vehicle object in sleep mode goes from transition to permanent. These states are described in detail in the Hibernate ORM manual.

By setting the brand on a saved vehicle, you expressly declare that you want to edit an existing row in the database, and DO NOT want you to create a new one. If you want to create a new one, follow the directions in the Hibernate manual and change the second stanza to:

 Vehicle vehicle2 = new Vehicle(otherMake, model, regNumber, abiCode) risk = new Risk(vehicle2); session.save(risk); 
+2
source

One possible solution is to override the SQL update for Vehicle with native SQL.

here is an example for a person:

 <class name="Person"> <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert> <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update> <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete> </class> 

So can you just override sql-uptade to embed instead?

+2
source

I think you need to take a look at Hibernate Envers (http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch15.html)

+2
source
 This Statment Will Help You SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); session.saveOrUpdate(object); transaction.commit(); 
+1
source

Use Session.evict. After deleting an object, it will not be updated in the database:

 Entity entity = .... session.evict(entity); entity.setA(a); // won't affect the database 

Thus, an object is not updated in the database unless you explicitly call “update” or “merge” (or “saveOrUpdate”).

+1
source

Sleep mode is built using listeners, there are lists for each type of activity in sleep mode eco system

DefaultSaveOrUpdateEventListener used to save or update objects using sleep mode, the onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent) method onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent) will contain information about the object that is being saved. when you call session.save(modelObject) , your call will end in the listener, here you can set the identifier to null, and the system will add a new record each time.

here is an example here is a blog post detailing the use of the listener

<property name="eventListeners"> <map> <entry key="save-update"><ref local="saveUpdateListener" /></entry> </map> </property>

+1
source

It would be helpful to know the context of your project and what you are ultimately trying to achieve.

The solution to your problem may lie at a different level than the technological trick you are asking for.

I suspect that you want to track changes in some way, that is, maintain a control chain of history or . You can do this with Envers for sleep mode. It provides the @Audited annotation and allows you to query historical data.

Otherwise, you can think of your classes for immutability . No organization should be volatile. Any change in essence creates a new object (sometimes called a permanent data structure in the functional world). If desired, a new object can be attached to the session automatically.

0
source

Source: https://habr.com/ru/post/1403053/


All Articles