Let's say I have an entity bean AccountBean with version field (javax.persistence.Version annotation). During transaction my application modifies this entity and performs database operations on other entities (inserts and updates rows). Some of those entity beans have #Version field but not all of them.
When the same AccountBean entity is modified concurrently by 2 threads, OptimistickLockException is thrown and (at least according to server log) the transaction is rolled back. However, only changes made to the conflicted AccountBean entity are actually rolled back - everything else is committed to database.
**EDIT: **
I added simple source code to illutrate the issue; The application is a REST web service; Two test threads call concurrently operation "update" with the same account id. Once again the OLE is thrown and yet the supposedly rolled back transaction commits to data base new AccountHistory entity :/
Since transactions are managed by container the transaction is started when method update is called and commited when it retursn value; That's also when OLE is thrown.
//UpdateAccount.java
#Stateless
#Path("account")
public class UpdateAccount {
#PersistenceContext
EntityManager em;
#Path("update")
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String update(Long accountId) {
Account account = em.find(Account.class, accountId);
if(null == account) {
return "account not found";
} else {
return executeUpdate(account);
}
}
String executeUpdate(Account account) {
Integer newValue = account.getValue() + 1;
em.persist(getAccountHistory(account, newValue));
account.setValue(newValue);
return "ok";
}
AccountHistory getAccountHistory(Account account, Integer newValue) {
AccountHistory history = new AccountHistory();
history.setId(new Date().getTime());
history.setAccount(account);
history.setValueBefore(account.getValue());
history.setValueAfter(newValue);
return history;
}
}
//Account.java
#Entity
public class Account {
#Id
private Long id;
#Column
private Integer value;
#Version
private Long version;
(...)//getters, setters etc
}
//AccountHistory.java
#Entity
public class AccountHistory {
#Id
private Long id;
#Column
private Integer valueBefore;
#Column
private Integer valueAfter;
#ManyToOne
#JoinColumn(name = "idaccount")
private Account account;
(...)//getters, setters etc
}
Am I wrong to expect all the changes rolled back?
Can I manually force complete roll back? I tried manually managing the transaction,
catching OLE and than calling rollback (as described on Adam Bien's
blog). However, when I catch the exception the transaction is
already marked as rolled back.
I deploy my application on jboss-eap-6.1/jboss-as-7.1.1Final with JRE 1.7, and use Hibernate (version defaults for those servers). My persistence.xml file is as simple as it gets. I haven't set any extra properties.
When the transaction roll backs, all the changes made within that transaction will be roll backed as well.
For local transaction this is handled by the JDBC connection, and for global ones by rolling back all enlisted JDBC transactions.
So, Hibernate doesn't control what gets roll backed or not. It's the underlying transaction manager that does the trick.
The only case when some changes are committed and some are roll-backed is when your service code uses multiple transactions (e.g nested transactions, REQUIRES_NEW) or when there are two consecutive transactional services being called from a non-transactional one. So if the second rolls back the first is committed anyway, because the first service is not transactional, hence any successive service call is enlisted in it's own transaction.
Related
I have some uncatchable bug in my work.
For example, I have code that looks like this:
#Entity
public class Message {
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "message_generator")
private long id;
private long massMessageId;
}
public class MessageDTO {
public final long id;
public final long massMessageId;
}
#Transactional
#Service
public class ExtendedMessageService {
private MessageService messageService;
public MessageDTO createMessage(MessageCreateDTO createDTO) {
var messageDTO = messageService.create();
return messageService.linkMassMessage(messageDTO.id, createDTO.massMessageId);
}
}
#Transactional
#Service
public class MessageService {
private final MessageRepository repository;
private final ObjectMapper mapper;
public MessageDTO create() {
var message = new Message();
var savedMessage = repository.save(message);
return mapper.map(savedMessage, MessageDTO.class);
}
public MessageDTO linkMassMessage(long messageId, long massMessageId) {
var message = repository.findById(messageId)
.orElseThrow(() -> new ObjectNotFoundException("Message with id " + id + " was not found"));
return mapper.map(repository.save(message.setMassMessageId(massMessageId)), MessageDTO.class);
}
}
What will happen in this situation? I have some bugs, when repository.findById(id) can't find entity and throws exception.
And i have no reason, why this bug is only on prod (i tried to repeat it on dev and nothing succeeded)
And when i try to find the reason of it, i get a question:
"Can i save entity and get it in one transaction in Spring?"
How saving works
repository.save() doesn't save anything to database, this method puts entity to the session (persistent context) in memory.
flush step — on this step actual SQL insert happens. It can be invoked manually repository.saveAndFlush(), repository.flush(). Hibernate can do flush in the background, before operations that can use saved to the database value, like JPQL statements.
Also flush happens when the end of #Transactional boundary is reached.
What can be an issue
You are using incorrect method. This method from the old version of Spring data and it doesn't perform search in the database. You have to use findById() method instead.
Hibernate: findById vs getbyId
The most simple way, if you want to use id after save — flush the data immediately.
Entity entity = new Entity(some_information);
repository.saveAndFlush(entity);
Entity findedEntity = repository.findById(entity.getId())
.orElseThrow(() -> new RuntimeException("Can't find id=" + entity.getId()));
Hibernate will not necessary perform SQL select to get findedEntity. It can get it from the session, if it happens in the same #Transactional boundaries.
So if the above code resides in the method with #Transaction SQL will not performed. if there is not #Transaction SQL will be performed.
About this question
"Can Spring or Hibernate find not flushed entity in transaction context? Or there are some other ways to do it?"
Hibernate can't find not flushed entity. if id is autogenerated, Hibernate needs to perform SQL INSERT (flush) to get the id from a database. Another option to set up an id manually. Probably in this case it will be possible to get an entity from the persistent context.
I am updating my application from Spring Boot 1.4.5 / Hibernate 4.3.5 to Spring Boot 2.0.9 / Hibernate 5.2.18 and code that used to work in the previous configuration is no longer working.
The scenario is as follows:
Start a transaction by entering a method annotated with #Transactional
Hydrate the entity
Change the entity
Make another query
Detect a problem. As a result of this problem, determine that changes should not persist.
Evict the entity
Exit the method / transaction
With Hibernate 4.3.5, calling entityManager.detach() would prevent the changes from being persisted. However, with Hibernate 5.2.18, I'm finding that changes are persisted even with this call. I have also tried to evict() from the session and I have tried to clear() all entities from the session (just to see what would happen).
So I ask - is it possible to discard entity changes in Hibernate 5.2.18 the way that I was able to do in Hibernate 4.3.5?
The relevant code is below...
#Entity
public class Agreement {
private Long agreementId;
private Integer agreementStateId;
#Id
#Column(name = "agreement_id")
public Long getAgreementId() {
return agreementId;
}
public void setAgreementId(Long agreementId) {
this.agreementId = agreementId;
}
#Basic
#Column(name = "agreement_state_id", nullable = false)
public Integer getAgreementStateId() {
return agreementStateId;
}
public void setAgreementStateId(Integer agreementStateId) {
this.agreementStateId = agreementStateId;
}
}
#Component
public class Repo1 {
#PersistenceContext(unitName = "rights")
private EntityManager entityManager;
public void evict(Object entity) {
entityManager.detach(entity);
}
public Agreement getAgreement(Long agreementId) {
// Code to get entity is here.
// Agreement with an agreementStateId of 5 is returned.
}
public void anotherQuery() {
// Code to make another query is here.
}
}
#Component
public class Service1 {
#Autowired
Repo1 repo;
#Transactional
public void doSomething() {
Agreement agreement = repo.getAgreement(1L);
// Change agreementStateId. Very simple for purposes of example.
agreement.setAgreementStateId(100);
// Make another query
repo.anotherQuery();
// Detect a problem here. Simplified for purposes of example.
if (agreement.getAgreementStateId() == 100) {
repo.evict(agreement);
}
}
}
I have found the problem and it has nothing to do with evict(). It turns out that an additional query was causing the session to flush prior to the evict() call.
In general, the application uses QueryDSL to make queries. Queries made in this way did not result in the session flushing prior to making a query. However in this case, the query was created via Session.createSQLQuery(). This uses the FlushMode already assigned to the session which was FlushMode.AUTO.
I was able to prevent the flush by calling setHibernateFlushMode(FlushMode.COMMIT) on the query prior to making the query. This causes the session FlushMode to temporarily change until after the query has been run. After that, the evict() call worked as expected.
i was just working on #EmbededId code, i want to do an auto increment before the entity is persisted, this is want to do without use of #GeneratedValue and an identity column,
below is the table with composite id,
create table TBL_EMPLOYEE_002(
ID integer,
COUNTRY varchar(50),
NAME varchar(50),
constraint PK_EMP_00240 primary key(ID,COUNTRY)
)
this is the code for Entity mapping,
#Entity
#Table(name="TBL_EMPLOYEE_002")
public class EmployeeEntitySix implements Serializable{
// contructor's
#EmbeddedId
private EmployeeIdTwo id;
#Column(name="NAME")
private String employeeName;
// getters and setter's
#PrePersist
public void incId(){
EntityManager em = null;
Query q = null;
EntityManagerFactory emf = null;
try{
emf = Persistence.createEntityManagerFactory("forPractise");
em = emf.createEntityManager();
q = em.createQuery("select max(e.id.employeeId) from EmployeeEntitySix e");
List list = q.getResultList();
Integer i = (list != null && list.size() > 0) ? Integer.valueOf(list.get(0).toString()) : 0;
this.getId().setEmployeeId(++i);
}catch(Exception e){
System.out.println("EXCETION WHILE INCREASING COUNTER...");
e.printStackTrace();
}finally{
if(em != null && em.isOpen()){
em.close();
}
if(getEmf() != null && getEmf().isOpen()){
getEmf().close();
}
}
}
This is the composite id mapping,
#Embeddable
public class EmployeeIdTwo implements Serializable{
#Column(name="ID")
private Integer employeeId;
#Column(name="COUNTRY",length=50)
private String empCountry;
// getters and setters
}
this code is of my main method, this main method is in some other class,
public static void main(String [] args){
EntityManagerFactory emf = null;
EntityManager em = null;
EntityTransaction tx = null;
try{
emf = Persistence.createEntityManagerFactory("forPractise");
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
EmployeeEntitySix employee = new EmployeeEntitySix(new EmployeeIdTwo("ZIMBABWE"), "Henry Olanga");
em.persist(employee);
....
}
Now the above code runs fine,
whenever i persist the entity "EmployeeEntitySix", the method annotated with #PerPersist runs, which will first fetch the max id, increments its, set it into the id in the embeded entity and persist the entity.
Now my question is,
I am creating EntityManagerFactory twice,
first in the main method,
second time in the #PrePersist method in entity EmployeeEntitySix. So whether i can use the first Entitymanagerfactory created in main method in the entity EmployeeEntitySix while pre-persist, or else whether i can reuse the entitymanager created in first time in main method in the #PrePersist method in entity.
Just for information, I am using plain java environment, I am not using a Java EE container.
Hibernate by default tries to persist all fields of an entity class or embedded id, including the field emf, but it does not know how to persist a field of the type EntityManagerFactory.
Of course it does not make sense to persist an EntityManagerFactory. You could mark the field as #Transient to prevent it from being persisted, but then you are just going to face different problems.
The injection of an EntityManagerFactory with a #PersistenceUnit annotation only works on CDI Beans and EJBs in applications that run on a Java EE-compliant application server. As you are using a main method, I assume that your example is a simple JSE program.
Furthermore you should not access EntityManagers in lifecycle callback methods such as #PrePersist. A quote from the JPA Specification (JSR 317: JavaTM Persistence API, Version 2.0):
In general, the lifecycle method of a portable application should not invoke EntityManager
or Query operations, access other entity instances, or modify relationships within the
same persistence context. A lifecycle callback method may modify the non-relationship
state of the entity on which it is invoked.
I suggest that you keep the EntityManagerFactory out of your embedded id class and also get rid of the incId-Method. Instead you could execute the query to determine the new employeeId in your main method, before calling persist. This works fine as long as only one instance of the program works with the database. When there are multiple programs trying to insert new employees there could be race conditions where the two programs try to insert the same id.
In order to prevent this you can use a database sequence to generate the employeeId, with the annotations #GeneratedValue and #SequenceGenerator. You find more information about id generation here: http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing
I'm using spring 3 with hibernate 3.5.4
1- I want to create an object in transaction and save it to DB ( which passes successfully ).
2- I want to update some fields in that object (same object) and updates in in DB in another transaction (and here is the problem).
The problem is, is saves the object successfully in the first transaction but it doesn't update it in DB in the second one.
here is code example:
public String entry(String str){
Bill b = create(str);
b = update(b);
b = updateAgain(b);
return "DONE";
}
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
public Bill create(String num){
Bill bill = new Bill();
bill.setBillNumber(num);
baseDao.saveObject(bill);
return bill;
}
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
public Bill update(Bill bill){
bill.setRetailAmount(152.0);
baseDao.saveObject(bill);
return bill;
}
NOTE: I don't want to put the #transactional annotation on method "entry".
Thanks,
The annotation will not take affect if called on a method within the same class. AOP cannot intercept that through proxy. Move your entry method outside the class.
EDIT: Spring enables the Transactional annotation via annotation-driven AOP with proxies or sub-classing. When enabled with proxies, your proxy is out of the picture in a local method call. This blog post has a good explanation with pictures.
I'm trying to maintain state across multiple calls by using an EXTENDED_PERSISTENT_CONTEXT. My understanding is that managed entities will not detach between calls however I keep getting errors related to detached entities in calls after I have previously thrown validation errors. The state is being maintained in a stateful session bean:
#Named(SessionFacadeBean.SEAM_NAME)
#SessionScoped
#Stateful
#LocalBean
#AccessTimeout(value = 10, unit = TimeUnit.SECONDS)
public class SessionFacadeBean implements Serializable
{
public static final String SEAM_NAME = "sessionCacheBean";
#PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME, type = PersistenceContextType.EXTENDED)
private EntityManager em;
private ParentOne sessionData;
public synchronized ParentOne getSessionData() {
if(sessionData == null) {
sessionData = new ChildTwo();
}
return sessionData;
}
public boolean getLock() {
return true;
}
public void clearLock() {
}
// Other stuff I don’t ‘think’ is relevant.
}
The (simplified) state is being stored using hibernate. It consists of three classes (a parent, and two children, one of which contains a list of children):
#XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
#XmlAccessorType(XmlAccessType.NONE)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "Class", length = 50)
#Entity
public class ParentOne
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#XmlElement(name = "ID")
private Long iD;
#XmlElement(name = "name")
protected String friendlyName = "";
}
#XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
#XmlAccessorType(XmlAccessType.NONE)
#Entity
public class ChildOne extends ParentOne
{
public ChildOne(String name, ParentOne child) {
super(name);
myChild = child;
}
#ManyToOne(cascade = CascadeType.ALL)
protected ParentOne myChild;
}
#XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
#XmlAccessorType(XmlAccessType.NONE)
#Entity
public class ChildTwo extends ParentOne
{
public ChildTwo() {
super(“common”);
}
}
I’m accessing the stateful bean from a stateless bean like so:
#Stateless
#LocalBean
#Path("/")
public class MyService
{
#PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME)
private EntityManager em;
#Inject
private SessionFacadeBean sessionBean;
#POST
#Path("/create/item")
#ValidateRequest
public ComponentShortSummary addItem(#Form NewItemForm itemForm)
{
if(sessionBean.getLock()) {
try {
if(itemForm.getName().equals("INVALID") == true) {
throw new ConstraintViolationException("Failed", new HashSet<ConstraintViolation<?>>());
}
ChildOne child = new ChildOne(itemForm.getName(), sessionBean.getSessionData());
em.persist(child);
return null;
}
finally {
sessionBean.clearLock();
}
} else {
return null;
}
}
}
To reproduce the problem, I perform the following sequence:
Call addItem with a valid name (this persists the item to the database).
Call addItem with a name ‘INVALID’, this throws the constraint exception.
Call addItem with a valid name (this results in a detached entity error on the line em.persist(child).
What I don’t understand is how/why I’m ending up with detached entities. In the real code, I would be performing some request / state validation, before modifying the state (so there is no reason that I can see for the state to have been detached).
If I remove the call to sessionBean.getLock() then the problem goes away (the objects persist correctly). The purpose of the lock methods is essentially to serialize access to the session state, however currently the getLock() method is empty, it feels like the problem might be related to the fact that I’m calling into the stateful bean before throwing the exception.
Can anybody explain what’s going on that results in my entities becoming detached / if there is a way to avoid it (and ideally point me at any documentation that supports the explanation)?
Whilst there are probably ways that I can work around the current issue, performing validation before accessing the stateful bean at all, I’m concerned about the general case (where any exception is thrown after the stateful bean has been accessed in the call). Is there an accepted strategy for dealing with exceptions when I don’t want the entities from the extended persistent context to be detached?
It looks like this is expected behaviour. Thanks to Scott Marlow's reference to the JPA spec, section 3.3.2.
Transaction Rollback
For both transaction-scoped and extended
persistence contexts, transaction rollback causes all pre-existing
managed instances and removed instances[31] to become detached. The
instances’ state will be the state of the instances at the point at
which the transaction was rolled back. Transaction rollback typically
causes the persistence context to be in an inconsistent state at the
point of rollback. In particular, the state of version attributes and
generated state (e.g., generated primary keys) may be inconsistent.
Instances that were formerly managed by the persistence context
(including new instances that were made persistent in that
transaction) may therefore not be reusable in the same manner as other
detached objects—for example, they may fail when passed to the merge
operation.[32]
So, entities that are involved in the active transaction are detached when the transaction is rolled back and by calling out to the sessionBean I am involving it in the transaction.
One way around this appears to be to decorate acceptable exceptions with the #AppicationException annotation. This marks the exception as non-fatal and prevents the transaction from being rolled back. This approach is described in some detail by David Blevin.