I have a batch operation where in i have to either insert or update a record.I want to inser larger number of records so i need to commit batch after batch
1)Insert if new
2)Update if existing.
I can typically do it using
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.saveOrUpdat(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
The problem is hibernate generates a select before each saveOrUpdate which seems to be a issue.
The primarykey of object is always populated before passing to hibernate.As it primaryKey is never generated by hibernate using sequencer or anything else.
How can i avoid this exta select for each saveOrupdate?
I dnt want to use stored procedure.
Following are the steps that it takes for Hibernate to decide whether to update or insert a record in to database.
saveOrUpdate() does the following:
if the object is already persistent in this session, do nothing
if another object associated with the session has the same identifier, throw an exception
if the object has no identifier property, save() it
if the object's identifier has the value assigned to a newly instantiated object, save() it
if the object is versioned by a <version> or <timestamp>, and the version property value is the same value assigned to a newly instantiated object, save() it
otherwise update() the object.
If in any case there is a conflict and hibernate is not able to decide on what operation to perform it does a select.
Coming to your question, try giving an hint to Hibernate like using the fields like timestamp or version
Credits - Jboss HIbernate Docs, StackOverFlow
Related
I am working on a Spring-MVC project in which I am using Hibernate as the ORM, PostgreSQL as our DB and in one of our Objects(GroupCanvas), we have a number which is incremented everytime when user takes some action, and then the GroupCanvas object is updated in DB, and it should be unique.
THe problem we have currently is, if multiple users take action in front-end, some of them are getting duplicate numbers. We are working on fixing this now, so later we can implement a sequence and are assured that the numbers are unique.
How can I ensure that when I am updating the row, other users are waiting till the row is updated. I tried LockMode.Pessimistic_write, and a few others, none helped.
Code :
#Override
public void incrementNoteCounterForGroupCanvas(int canvasId) {
Session session = this.sessionFactory.getCurrentSession();
session.flush();
Query query = session.createQuery("update GroupCanvas as gc set gc.noteCount=gc.noteCount+1 where gc.mcanvasid=:canvasId");
query.setParameter("canvasId",canvasId);
query.executeUpdate();
session.flush();
}
#Override
public GroupCanvas getCanvasById(int mcanvasid) {
Session session = this.sessionFactory.getCurrentSession();
session.flush();
return (GroupCanvas) session.get(GroupCanvas.class, mcanvasid,LockMode.PESSIMISTIC_WRITE);
}
Both methods are in DAO, which has #Transactional annotation, and annotation present in service layer as well.
Thank you.
Looking at the method you have posted the usage if the 'LOCKING' technique is not quite correct. In order for a lock to end up with the result you are looking for the sequence of actions should be similar to the ones below (in the nutshell it is similar to the Double-Checked Locking but implemented using DB locks - https://en.wikipedia.org/wiki/Double-checked_locking).
Start the transaction (eg #Transactional annotation on your service method)
Retrieve entity from database with the PESSIMISTIC_WRITE lock mode (make sure to indicate hibernate that fresh copy should be read instead of the one stored in session cache)
If required check the current value of the target field if it meets your invariants
Perform the change/update on the field (eg, increment the value of a field )
Save the entity (and make sure to flush the value to the DB if you do not want to wait for the auto-flush)
Commit the transaction (done automatically when using #Transactional)
The essential difference of this sequence when compared with the posted method is that the update of the property value is performed while your transaction holds a lock on the target entity/db row, hence preventing other transactions from reading it while your update is in progress.
Hope this helps .
UPDATE:
I believe something like the code snippet bellow should work as expected :
#Transactional
#Override
public void incrementNoteCounterForGroupCanvas(int canvasId) {
final Session session = this.sessionFactory.getCurrentSession();
final GroupCanvas groupCanvas = session.get(GroupCanvas.class, canvasId,LockMode.PESSIMISTIC_WRITE);
session.refresh(groupCanvas);
groupCanvas.setNoteCount(groupCanvas.getNoteCount()+1);
session.saveOrUpdate(groupCanvas);
session.flush();
}
In hibernate, session.save() is supposed to save the records.It generates "insert" queries. However, I have written below simple program to check this. I am observing the save() can also be used to update the records. It is generating "update" query. Isn't save() doing the same job as saveorupdate() in the below program ?
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
Transaction trans = session.beginTransaction();
Vehicle veh = new Vehicle();
veh.setId(1);
veh.setModel("Veh_mod");
veh.setName("Veh_Name");
Serializable obj = session.save(veh);
veh.setModel("Veh_mod_change");
obj = session.save(veh);
session.flush();
trans.commit();
session.close();
------------------------- in the console--------------------------------
Hibernate:
/* insert com.anvesh.test.Vehicle
*/ insert
into
VEHICLE
(NAME, MODEL, ID)
values
(?, ?, ?)
Hibernate:
/* update
com.anvesh.test.Vehicle */ update
VEHICLE
set
NAME=?,
MODEL=?
where
ID=?
After your first call to save(), object veh becomes an attached object (aka. persistent object state). Subsequently mutating that object with setModel() and committing the transaction would cause hibernate to fire an update even without calling save() a second time.
Here's an example for reference: http://www.dineshonjava.com/p/transient-persistent-and-detached.html#.VEfGCme8G7E
Or perhaps a short video tutorial: http://javabrains.koushik.org/tutorials/hibernate_run/Hibernate-Tutorial-22---Transient,-Persistent-and-Detached-Objects.html
save() can do an update, if id is set on the object it saves. Check out this thread for differences between various saving methods. To quote from an accepted answer
save Persists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity.
When you first called session.save(veh), your object becomes associated with the session.
Hibernate will then know that it needs to use an "UPDATE" query when you save the object again.
Try:
Vehicle veh1 = new Vehicle();
veh1.setId(1);
veh1.setModel("Veh_mod");
veh1.setName("Veh_Name");
Vehicle veh2 = new Vehicle();
veh2.setId(1);
veh2.setModel("Veh_mod");
veh2.setName("Veh_Name");
session1.save(veh1);
session2.save(veh2); // try changing this to session2.saveOrUpdate()
then you'll see the difference between session.save() and session.saveOrUpdate()
The hibernate document says:
persist():
persist() makes a transient instance persistent. However, it does not guarantee that the identifier value will be assigned to the
persistent instance immediately, the assignment might happen at flush
time. persist() also guarantees that it will not execute an INSERT
statement if it is called outside of transaction boundaries. This is
useful in long-running conversations with an extended
Session/persistence context.
save():
save() does guarantee to return an identifier. If an INSERT has to be executed to get the identifier ( e.g. "identity" generator, not
"sequence"), this INSERT happens immediately, no matter if you are
inside or outside of a transaction. This is problematic in a
long-running conversation with an extended Session/persistence
context.
So I am trying with a small example on how this works. I created an entity called DomesticCat:
#Entity
public class DomesticCat {
#Id
#GeneratedValue
private long id;
private String name;
}
and a small program to test this, once using save() and another time with persist():
private static void saveData() {
Session session = getSession();
DomesticCat cat = new DomesticCat();
cat.setName("My Cat");
//session.save(cat);
session.persist(cat);
}
For this program, hibernate generated same queries for save & persist, in this case it is:
select hibernate_sequence.nextval from dual
Now I added an extra line to my code saying:
session.flush();
Now hibernate generated insert query for both cases i.e save & persist:
insert into CAT (name, id) values (?, ?)
Also when I do session.flush(), the id is getting assigned to my cat object when I use save() and also for persist()
Finally, when I use the transaction then the data is stored in the DB table.
So using this example I can see only single difference between persist vs save, that is save returns the identifier where as persist will not.
Then what exactly the document says, can someone please help me with some examples?
Update:
I am using Oracle as my database.
Now I modified my entity class Id generation strategy to increment as follows:
#Id
#GeneratedValue(generator="increment")
#GenericGenerator(name="increment", strategy = "increment")
private long id;
But even then I can see that calling session.persist() is hitting the DB to get the Id value. Here is my program and its output:
private static void saveData() {
Session session = getSession();
DomesticCat cat = new DomesticCat();
cat.setName("My Cat");
System.out.println("before id="+cat.getId());
session.persist(cat);
System.out.println("after id="+cat.getId());
session.flush();
System.out.println("after flush id="+cat.getId());
}
Output:
before id=0
Hibernate: select max(id) from CAT
after id=1
Hibernate: insert into CAT (name, id) values(?, ?)
after flush id=1
As per the output hibernate is hitting the DB to get the ID before I call session.flush() and the case is same for session.save() also. So there is no difference in output if I use Id generation strategy to increment.
All the information is in the documentation. save() flushes the entity to the database when you make the call. persist() actually just marks the entity to be persisted in the upcoming flush. There is a difference and with persist you have more control on when the actual write to the database takes place.
It's a simple logic problem.
it does not guarantee that the identifier value will be assigned to the persistent instance immediately
is not the same thing as
it guarantees that the identifier value will not be assigned to the persistent instance immediately
The ID generation strategy used for your database is to use a sequence. In that case, Hibernate asks the sequence for the next ID when calling persist(). If the ID generation strategy for your database was to use an auto-increment column, the ID would only be assigned to the entity only when the entity is inserted to the database, at flush time.
I am using #Version annotation to provide version control in hibernate. My question is regarding the proper mapping of data from DTO to Entity.
What I feel is the right way is as follows but I want to know if there is a better way or this is how everybody does it.
call comes to my service
i load the entity to be updated (assume AddressEntity with version = 1)
i map the AddressDTO values to AE, including sub-collections if any
after all mapped, i detach the entity AE (only to be detached after Lazy sub collections mapped too)
now i map the version from DTO to AE (as hibernate does not allow to update version in managed entity)
now i call merge to update this detached AE entity
1) Is this the right way semantics and logic wise ?
2) (bit out of context) is there an overhead for hibernate to merge an object already in context and managed ie can i use merge for all updates safely irrespective or managed/unmanaged or Only merge+flush for unmanaged and flush for managed after updating some properties ?
Let me try to answer your question stepwise:
Suppose you have loaded an AddressEntity (having id=123 and version=1). Set the property values from AddressEntity to AddreeDto including the id and version values. Send the AddressDto to UI.
Changes made to AddresDto. Call has come to your service. Create an instance of AddressEntity and set the values from AddressDto including the id and version values. This new AddressEntity has now turned into a detached instance, as it has a persistent identity, but its state is not guaranteed to be synchronized with database state.
Hibernate lets you reuse this Addressentity instance in a new transaction by reassociating it with a new persistence manager.This detached instance can be reassociated with a new Session by calling update(). You don't need to load the entity again.The update() method forces an update to the persistent state of the object in the database.
Set the addressEntity properties:
addressEntity.setId(dto.getId());
addressEntity.setVersion(dto.getVersion());
Attach addressEntity to a new session:
Transaction tx = sessionTwo.beginTransaction();
sessionTwo.update(addressEntity);
tx.commit();
sessionTwo.close();
The session.update will execute an SQL similar to this:
update ADDRESS_ENTITY set ... , VERSION=2
where ID=123 and VERSION=1
If another application transaction would have updated the same ADDRESS_ENTITY since it was loaded, the VERSION column would not contain the value 1, and the row would not be updated, and you will receive a stale object state exception. You can catch the exception and inform the User about the stale data.
after all mapped, i detach the entity AE (only to be detached after Lazy sub collections mapped too)
Assuming you are performing this in a single transaction. Any persistent object that you have retrieved from DB is associated with the current session and transaction context. If it is modified in the same transaction, its state will be automatically synchronized with the DB. This mechanism is called automatic dirty checking. It means Hibernate will track and save the changes made to an object inside a session.
Transaction tx = session.beginTransaction();
int addressEntityID = 1234;
AddressEntity addressEntity = (AddressEntity) session.get(AddressEntity.class, new Long(addressEntityID));
// set the values from AddressDTO to AddressEntity
tx.commit();
session.close();
The object is retrieved from DB, it is modified and the modifications are propagated to DB on transaction commit.You don't need to detach and reattach an entity to perform an update.
now i map the version from DTO to AE (as hibernate does not allow to update version in managed entity)
The managed versioning is used to implement optimistic locking and the versioning of the entities is managed by Hibernate. The version number is just a counter value, it does not have any useful information that you should keep in your DTO.You don’t need to set the value of the version yourself. Hibernate will initialize the value when you first save an AddressEntity, and increment or reset it whenever the object is modified.
If another application transaction(T2) updates the persistent instance the same item since it was read by the current application transaction(T1), the T2 transaction will change the version value for this entity. Now when T1 tries to make an update, Hibernate will throw a stale object state exception, as the version of the entity has been changed. You can catch the exception and inform the User about the stale data. In particular, versioning prevents the lost update problem. You don't need to map the version from DTO to AE or from AE to DTO, as it does not have any meaningful information which can be used in contexts other than to implement optimistic locking.
i've created the Hibernate project using Spring Template Project. Two domain objects, a JUnit test, app-context.xml and the persistence-context.xml were created. Now i noticed this line
<jdbc:embedded-database
id="dataSource"></jdbc:embedded-database>
and assume that the following happens
A default HQSL db is used
The two created models Order.java & Item.java will automatically created in memory tables T_ORDER and T_ITEM and the these will be mapped as per annotations on the objects. Inside the auto created classes one of the test methods is as follows
#Test
#Transactional
public void testSaveAndGet() throws Exception {
Session session = sessionFactory.getCurrentSession();
Order order = new Order();
order.getItems().add(new Item());
session.save(order);
session.flush();
// Otherwise the query returns the existing order
// (and we didn't set the parent in the item)...
session.clear();
Order other = (Order) session.get(Order.class, order.getId());
assertEquals(1, other.getItems().size());
assertEquals(other, other.getItems().iterator().next().getOrder());
}
Questions ...
Am i correct to think that the in memory tables are created from the domain models (Order/Item), and mapped? Therefore session.flush() synchronize the object to the physical (in memory table)....
Are these tables auto mapped because if i do the following
session.save(order);
session.flush();
session.clear();
Order other = (Order) session
.createQuery("from T_ORDER where ORDER_ID =: orderid")
.setLong("orderid", order.getId())
.uniqueResult();
i get an exception...
org.hibernate.hql.ast.[B]QuerySyntaxException[/B]: \
T_ORDER is not mapped [from T_ORDER where ORDER_ID =: orderid]
............
............
if these tables are not mapped automatically, how is flushing working at the first place?
Table creation is a feature of Hibernate (and other JPA proviers). It taking place when the application/test starts. It has nothing to do with any query. Even if you only start your test, with Hibernate running and configured, it can create the tables.
If Hibernate create the tables, drop old once, and so on, depends on its configuration: the property: hibernate.hbm2ddl.auto is used what hibernate do if its starts. For example the value update will add not existing tables and columns.
More details can be found in the documentation.
Your Exception
When you uses Hibernate and write hibernate query statements, then you have to use HQL and not SQL. -- The main difference is that HQL is based on the classes but not on the tables. So in your case you must not use T_ORDER, but Order (the same is for the id, you need to use the property/field name, but not the column name).