I have two table and want both table must update and save altogether otherwise rollback. i am using Transaction but not getting expected result when found runtime exception. Table_1 got updated and table_2 was not updated after getting some conversion issues in my java code.
Service Class
public void startTransaction(){
try{
transaction.begin();
//Calls manager's class method to update and save the data into table
transaction.commit();
}catch(Exceptio e){
transaction.rollback();
}
}
Manager class
public void updateAndSave(){
try{
-> calls DAO method for table_1
->Update Table_1 via DAO update method
-> calls DAO method for table_2
->Update Table_2 via DAO update method
}catch(Exception e){
......
}
}
Related
For a simple batch update of a MariaDB table, properly mapped as a Hibernate entity class, a simple update via Hibernate produces the error
org.hibernate.StaleStateException: Batch update returned unexpected row count from update
Each table record is modeled by an Entity class, which is a simple POJO that needs to be updated (if it already exists) or inserted as a new object (if it does not exist in the table), with a primary id field (not auto-incremented) and some other values, all scalar. The error can be reproduced by the following method.
public static void update(Set<Long> ids) {
Session session = createSession();
Transaction t = session.beginTransaction();
try {
for (Long id : ids) {
Entity entity = session.get(Entity.class, id);
if (entity == null) {
entity = new Entity();
}
entity.setId(id);
// Other entity value settings
session.saveOrUpdate(entity);
}
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
}
What is the correct way of implementing the above operation in Hibernate?
You are using saveOrUpdate() in this way Hibernate decides by his own logic what is a new (Transient) and what is an old (Persisted) object and depends on this performs save() or update() method accordingly.
Hibernate assumes that an instance is an unsaved transient instance if:
The identifier property is null.
The version or timestamp property (if it exists) is null.
A new instance of the same persistent class, created by Hibernate internally, has the same database identifier values as the given instance.
You supply an unsaved-value in the mapping document for the class, and the value of the identifier property matches. The unsaved-value attribute is also available for version and timestamp mapping elements.
Entity data with the same identifier value isn't in the second-level cache.
You supply an implementation or org.hibernate.Interceptor and return Boolean.TRUE from Interceptor.isUnsaved() after checking the instance in your code.
Otherwise: entity will be determined like already saved persisted
In your example, Hibernate did not determine the new (Transient) object and as result, perform update() method for it. It produced UPDATE instead of INSERT statement. UPDATE statement for not existing record returns zero updated records, so it is the reason for your exception.
Solution: explicitly use save() method for new entities:
public void update(Set<Long> ids) {
Session session = getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
try {
for (Long id : ids) {
HibernateEntity entity = session.get(HibernateEntity.class, id);
if (entity == null) {
entity = new HibernateEntity();
}
// Other entity value settings
entity.setValue(entity.getValue() + "modification");
if (entity.getId() == null) {
entity.setId(id);
session.save(entity);
}
}
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
}
update() method is not required to call explicitly. Transactional persistent instances (i.e. objects loaded, saved, created or queried by the Session) can be manipulated by the application, and any changes to persistent state will be persisted when the Session is flushed. According to documentation.
I want to test that my controller endpoint returns an appropriate error code when trying to delete a record with referencing child records. In my integration test, I need to set up the state so that the related records exist, then invoke the deletion endpoint, expect the error condition, and then (ideally) roll the entire DB back to the state it was in before the test.
e.g.
INSERT INTO parent_rec (id) VALUES ("foo");
INSERT INTO child_rec (id, parent_id) VALUES ("bar", "foo");
COMMIT;
DELETE FROM parent_rec WHERE id = "foo"; -- bang!
#PersistenceContext
EntityManager em;
#Transactional
void testDelete() {
// Set up records
ParentRecord record = new ParentRecord("foo");
em.persist(record);
em.persist(new ChildRecord("bar", record));
//delete
mockMvc.perform(delete("/parent/foo")).andExpect(/* some error code */);
}
However, I'm running into issues. If I put the #Transactional annotation at the method or class level, the records aren't persisted until after the deletion is attempted so the deletion returns a 200 OK rather than a 400 Bad Request or similar.
The current solution is for the tests to be run in order (with a previous test setting up records which a subsequent test tries to operate on). However, this makes the tests pretty brittle and dependent on each other, which I'd like to avoid primarily to make changing the code easier.
Can I accomplish what I want without using an additional layer of tooling? In the past, I'd have used DBUnit to do something like this, but if I can avoid adding the additional dependency I'd prefer to keep it simple.
In JEE I solved these issues kind of simply by splitting my code into two parts:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class ParentRecordTestFacade {
public void create() {
// Create record here
}
public void delete() {
// Delete record here
}
}
and then call both methods in the actual unit test one after another.
Running only some code in a separate transaction also comes in handy. You can achieve it for example by creating a method fo the block of code to invoke in transaction:
protected <T> T getInsideTransaction(Function<EntityManager, T> transactional) {
EntityManager em = null;
EntityTransaction trx = null;
try {
em = entityManagerFactory.createEntityManager();
trx = em.getTransaction();
trx.begin();
return transactional.apply(em);
} catch (Throwable throwable) {
throw throwable;
} finally {
if (trx != null) {
if (!trx.getRollbackOnly()) {
trx.commit();
} else {
trx.rollback();
}
}
if (em != null) {
em.close();
}
}
}
Now you can invoke it like that:
void testDelete() {
// Set up records
getInsideTransaction(em -> {
ParentRecord record = new ParentRecord("foo");
em.persist(record);
em.persist(new ChildRecord("bar", record));
}
//delete
mockMvc.perform(delete("/parent/foo")).andExpect(/* some error code */);
}
You can invoke an arbitrary block of code within separate transaction that way.
In spring especially for test such cases in repository layer I using, looks like should works and for you - org.springframework.test.context.transaction.TestTransaction. Pay attention on #Commit annotation on test method, otherwise your record will not be saved.
#Commit
void testDelete() {
// Set up records
ParentRecord record = new ParentRecord("foo");
em.persist(record);
em.persist(new ChildRecord("bar", record));
TestTransaction.end()
TestTransaction.start()
//delete
mockMvc.perform(delete("/parent/foo")).andExpect(/* some error code */);
}
But of course after commit you should delete manually you record.
I have got a Springboot Application and a Oracle DB with lots of PL/SQL Procedures and these change the state of the DB all the Time.
So now I want to change a loaded entity an want to save it. If the entitystate of the entitymanager and the state of the db is equal everything works fine. But in some cases they are not equal. So if I load an entity and make some changes an druring this a PL/SQL Procedure changes the DB Table. If I save the Entity I will get an Execption of course. So I tried to catch the Exception and then in the catch block I want to refresh the Entity before saving it. But I still get an Exception. Is the Transaction not jet finished? How can I handle this Problem?
I hope the example code explains a little bit.
#RestController
#RequestMapping("/*")
public class FacadeController {
...
#ResponseStatus(HttpStatus.OK)
#RequestMapping( value= "/test4" , method=RequestMethod.GET)
public String test4(){
Unit unit = unitSvice.loadUnit(346497519L);
List<UnitEntry> entries = unit.getEntries();
for (UnitEntry g : entries) {
if (g.getUnitEntryId == 993610345L) {
g.setTag("AA");
g.setVersion(g.getVersion() + 1);
g.setstatus("SaveOrUpdate");
}
}
//<-- DB Table changed entity managed by entitymanger and DB Table
// are no langer equal.
try {
unitSvice.updateUnit(unit , false);
}catch(DataAccessException | IllegalArgumentException e) {
unitSvice.updateUnit(unit , true);
}
...
}
}
#Service("unitSvice")
public class UnitSvice {
#Autowired
private UnitDao repoUnit;
#Transactional
public Unit loadUnit(Long _id) {
Unit unit = repoUnit.findOne(_id);
return unit;
}
#Transactional
public void updateUnit(Unit unit, boolean _withrefrsh ) {
if(_withrefrsh) {
getEntityManager().refresh(unit.getId());
}
repoUnit.save(unit);
}
}
I hope, anyone can help me.
Thanks
yes the problem is ..when you call load all method which is transactional method where entities became detached from session/entitymanager when you are returning from that method.. so,next you are trying to persist detached object. That's why you get exception.
so probably you can use session.update() or session.merge() to save the new update into database.
I have been making a simple program to save an employee details in mysql using hibernate framework as follows...
public class Manifest {
Session session;
public static void main(String[] args) {
Employee employee = new Employee("varun");
new Manifest().addEmployee(employee);
}
/* Method to CREATE an employee in the database */
public void addEmployee(Employee employee){
Integer employeeID=null;
SessionGenerator sessionGenerator = new SessionGenerator();
try{
session = sessionGenerator.getSessionToDb();
employeeID = (Integer) session.save(employee);
System.out.println(employeeId);
}catch (HibernateException e) {
e.printStackTrace();
}finally {
session.close();
}
}
}
I am aware of the fact that I should use session.beginTransaction(); & tx.commit() but my question is that why no exception is thrown here in my case and it is printing employeeId on console but not making any entry in database.. What the reason behind that???
1) Session.save() may perform an INSERT outside transaction boundaries: save() method returns an identifier, and if an INSERT has to be executed to get the identifier (e.g. "identity" generator), this INSERT happens immediately (in order to get an identity), no matter if you are inside or outside of a transaction.
2) Commit might happen even without transaction if you set:
<property name="connection.autocommit">true</property>
that's why an exception is not raised
Check this article (Working nontransactionally with Hibernate): https://developer.jboss.org/wiki/Non-transactionalDataAccessAndTheAuto-commitMode
I have a method that receives a JPA Entityand its related EntityManager as parameters. The Entity instance is not created inside the class, and it might very well be shared by other classes (like GUIs and such).
The method starts a transaction, carries out some changes on the entity, and finally commits the transaction.
In case the commit fails, EntityTransaction.rollback() is called: in accordance with JPA specifications, the entity is then detached from the manager.
In case of failure the application needs to discard the pending changes, restore the original values inside the entity e and re-attach it to the EntityManager, so that the various scattered references to the e object would remain valid. The problem raises here: what I understood is that this is not a straightforward operation using the EntityManager's APIs:
calling EntityManager.refresh(e) is not possible since e is detached.
doing e = EntityManager.merge(e) would create a new instance for e: all the other references to the original e in the program at runtime would not be updated to the new instance. This is the main issue.
moreover (actually not quite sure about this), EntityManager.merge(e) would update the new managed instance's values with the values currently held by e (i.e., the values that probably caused the commit to fail). Instead, what I need is to reset them.
Sample code:
public void method(EntityManager em, Entity e) {
EntityTransaction et = em.getTransaction();
et.begin();
...
// apply some modifications to the entity's fields
...
try {
et.commit();
} catch (Exception e) {
et.rollback();
// now that 'e' is detached from the EntityManager, how can I:
// - refresh 'e', discarding all pending changes
// - without instantiating a copy (i.e. without using merge())
// - reattach it
}
}
What is the best approach in this case?
A possible solution would be like:
public class YourClass {
private EntityManager em = ...; // local entity manager
public void method(Entity e) { // only entity given here
Entity localEntity = em.find(Entity.class, e.getId());
EntityTransaction et = em.getTransaction();
et.begin();
...
// apply some modifications to the local entity's fields
applyChanges(localEntity);
...
try {
et.commit();
// Changes were successfully commited to the database. Also apply
// the changes on the original entity, so they are visible in GUI.
applyChanges(e);
} catch (Exception ex) {
et.rollback();
// original entity e remains unchanged
}
}
private void applyChanges(Entity e) {
...
}
}