Transaction required exception on execute update for JPQL update query - jpa

I get this error when I try to run this code.
Error:
javax.persistence.TransactionRequiredException: executeUpdate is not supported for a Query object obtained through non-transactional access of a container-managed transactional EntityManager
Code: (_ut is a UserTransaction object)
public void setMainCategory(Integer deptId, Integer catId) {
try {
Query setmain = _entityManager.createNamedQuery("Category.setAsMain");
Query removeMain = _entityManager.createNamedQuery("Category.removeMain");
setmain.setParameter("categoryId", catId);
Department d;
d=_entityManager.find(Department.class, deptId);
removeMain.setParameter("department", d);
_ut.begin();
removeMain.executeUpdate();
_ut.commit();
_ut.begin();
setmain.executeUpdate();
_ut.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
I have other functions that are identical in implementation and they do not throw this error.
Any suggestions would be greatly appreciated.
Thanks.

You are using an EntityManager to get the Named Queries and also using an (what I think is) injected UserTransaction.
See that the error message says "...Query object obtained through non-transactional access...". This means you are getting the "NamedQuery" through non-transactional access, because the EntityManager is not in the same transaction as _ut. So, you first join the EntityManager to the UserTransaction then you get and execute the query.
Finally your block should look like:
#PersistenceContext(unitName = "myPU")
EntityManager em;
#Inject
UserTransaction ut;
public void doSomeStuff()
{
ut.begin();
em.joinTransaction();
em.createNamedQuery("query1").executeUpdate();
ut.commit();
}

The problem does not come from your method implementation but from your execution context. All method that update database must be executed within an opened transaction. The way you ensure that depends on the way you manage transaction. If transactions are user-managed you have to explicitly retrieve and join an existing transaction or open a new one. If it's container-managed just add #transactional annotation on your method or ensure that there is a method in your call hierarchy holding the annotation.
Here you're using user-managed transactions in a container-managed transaction context (see "container-managed transactional EntityManager" in your error message). You shouldn't so begin and commit / rollback yourself the transactions. If you want to do so, just retrieve an application-managed EntityManager to be able to properly access to JTA transactions.
cf. http://docs.oracle.com/cd/E19226-01/820-7627/bnbqy/index.html

According to the error message, I think the reason is that your order of transaction, you didn't get the Department d in transaction, so the statement of d is detached, then you want to directly update it which will change the statement to managed, the JPA can't do this. just move the find code in the transaction, I think it will be ok.

Related

How id can be found in Transaction-Scoped Persistence context if it's not in the database

An example from Pro JPA:
#Stateless
public class AuditServiceBean implements AuditService {
#PersistenceContext(unitName = "EmployeeService")
EntityManager em;
public void logTransaction(int empId, String action) {
// verify employee number is valid
if (em.find(Employee.class, empId) == null) {
throw new IllegalArgumentException("Unknown employee id");
}
LogRecord lr = new LogRecord(empId, action);
em.persist(lr);
}
}
#Stateless
public class EmployeeServiceBean implements EmployeeService {
#PersistenceContext(unitName = "EmployeeService")
EntityManager em;
#EJB
AuditService audit;
public void createEmployee(Employee emp) {
em.persist(emp);
audit.logTransaction(emp.getId(), "created employee");
}
// ...
}
And the text:
Even though the newly created Employee is not yet in the database, the
audit bean can find the entity and verify that it exists. This works
because the two beans are actually sharing the same persistence
context.
As far as I understand Id is generated by the database. So how can emp.getId() be passed into audit.logTransaction() if the transaction has not been committed yet and id has not been not generated yet?
it depends on the strategy of GeneratedValue. if you use something like Sequence or Table strategy. usually, persistence provider assign the id to the entities( it has some reserved id based on allocation size) immediately after calling persist method.
but if you use IDENTITY strategy id different provider may act different. for example in hibernate, if you use Identity strategy, it performs the insert statement immediately and fill the id field of entity.
https://thoughts-on-java.org/jpa-generate-primary-keys/ says:
Hibernate requires a primary key value for each managed entity and
therefore has to perform the insert statement immediately.
but in eclipselink, if you use IDENTITY strategy, id will be assigned after flushing. so if you set flush mode to auto(or call flush method) you will have id after persist.
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Ids/GeneratedValue says:
There is a difference between using IDENTITY and other id generation
strategies: the identifier will not be accessible until after the
insert has occurred – it is the action of inserting that caused the
identifier generation. Due to the fact that insertion of entities is
most often deferred until the commit time, the identifier would not be
available until after the transaction has been flushed or committed.
in implementation UnitOfWorkChangeSet has a collection for new entities which will have no real identity until inserted.
// This collection holds the new objects which will have no real identity until inserted.
protected Map<Class, Map<ObjectChangeSet, ObjectChangeSet>> newObjectChangeSets;
JPA - Returning an auto generated id after persist() is a question that is related to eclipselink.
there are good points at https://forum.hibernate.org/viewtopic.php?p=2384011#p2384011
I am basically referring to some remarks in Java Persistence with
Hibernate. Hibernate's API guarantees that after a call to save() the
entity has an assigned database identifier. Depending on the id
generator type this means that Hibernate might have to issue an INSERT
statement before flush() or commit() is called. This can cause
problems at rollback time. There is a discussion about this on page
490 of Java Persistence with Hibernate.
In JPA persist() does not return a database identifier. For that
reason one could imagine that an implementation holds back the
generation of the identifier until flush or commit time.
Your approach might work fine for now, but you could run into troubles
when changing the id generator or JPA implementation (switching from
Hibernate to something else).
Maybe this is no issue for you, but I just thought I bring it up.

UnitofWork [aspnetboilerplate] Transaction Management

I am currently working on implementing aspnetboilerplate's transaction management
Below is the method I am using to insert a order and products associated with the order
public class OrderController
{
IOrderAppService _orderAppService;
public OrderController(IOrderAppService orderAppService)
{
_orderAppService = orderAppService;
}
public void TestOrder()
{
_orderAppService.TestTransaction();
}
}
public class OrderAppService : IOrderAppService
{
//repositories are injected here
public void TestTransaction()
{
//Created 'order' and 'products' here
//Committing the created objects
CommitOrderTransaction();
}
private void CommitOrderTransaction()
{
using (var unitOfWork = _unitOfWorkManager.Begin())
{
//Inserts the Order record
CommitInsertOrderHeader(); // Order Header is saved in database by using SaveChanges() method
//Inserts the Product records associated with OrderId
CommitInsertOrderDetails();
unitOfWork.Complete();
}
}
}
As the aspnetboilerplate documentation tells that,
"if current unit of work is transactional, all changes in the transaction are rolled back if an exception occurs, even saved changes."
In my case when an exception occurs on inserting the OrderDetails, I would like the header record to be rolled back as well but I still have the Order header record in database.
you don't need to handle transaction manually. ABP handles it for you! All application service methods are automatically set as UnitOfWork. It's an atomic operation. So if any exception occurs in the middle of transactions all the db operations are being rolled back.
further information check out https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work
If you are calling SaveChanges() twice and you aren't using a TransactionScope across both, then you won't be able to rollback the first call. I don't know what UnitOfWork is doing here, but if the DbContext you are working with isn't being used in that UoW, then nothing is going to happen. DbContext is technically its own Unit of Work already. You should be adding Orders and Order Details to the same DbContext and calling SaveChanges() just once. Then you'd be able to roll back both in that scenario.

JPA CriteriaDelete with huge parameters

I am using JPA 2.1, Oracle DB and have a list of ids for entities to be removed (about 430000 ids). At first, it was implemented as splitting that id list into each smaller one with 1000 ids, pass them as parameters for a JPQL and executing.
delete from SOPFilter f where f.id in (?1)
Then, I want to change to use JPA CriteriaDelete.
CriteriaDelete<SOPFilter> criteriaDelete = cb.createCriteriaDelete(SOPFilter.class);
Root<SOPFilter> from = criteriaDelete.from(SOPFilter.class);
criteriaDelete.where(from.get(SOPFilter_.id).in(sopFilterIds));
It runs fine until it reach the 90000th one and there is a runtime exception cause it to stop here
org.hibernate.SessionException: Session is closed
and make entity manager factory to close.
INFO : bernate.impl.StmpContainerEntityManagerFactoryBean: Closing JPA EntityManagerFactory for persistence unit 'IMOFFERINGMANAGEMENT'
For whom was mislead by my first post with this exception
java.lang.IllegalStateException: EntityManagerFactory is closed
There was a catch clause to handle runtime exception by adding a record to database before throwing it. And to add a event record, it attempts to create another entity manger from the factory which is closed now.
public static void logEvent(EntityManager em) {
EntityManager em2 = null;
EntityManagerFactory emFactory = em.getEntityManagerFactory();
em2 = emFactory.createEntityManager();
// ...
}
Could anyone shed some light on it?
Im not clear on your code but you are likely hitting a transaction timeout. You can set a hint -
query.setHint("javax.persistence.query.timeout", 8000);
There may also be timeouts on the database side

EJB - Using an EntityManager - Can finding an entity cause an OptimisticLockException

Unfortunately I'm getting an OptimisticLockException in my code and I'm not sure why. Perhaps there is someone who can help me with an answer to a general question.
Following scenario:
#Entity
public class MyEntity {
#Id
#GeneratedValue
private Integer id;
#Version
private int version;
private String value;
}
#Singleton
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyBean {
#PersistenceContext
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void test() {
MyEntity myEntity = em.find(MyEntity.class, 1);
}
}
CMT are used. Method test() requires a new transaction.
Now my question: Can method test() throw an OptimisticLockException if there is another thread in another bean using the same persistence context changing my entity before commit although I only use find and don't update anything in my method test()?
from this blog
JPA Optimistic locking allows anyone to read and update an entity, however a version check is made upon commit and an exception is thrown if the version was updated in the database since the entity was read
So there is no need to do an update to get an OptimisticLockingException. Assume myEntity.getVersion()==1 when you read it. You will have an OptimisticLockingException if, at commit (i.e. when your test() method ends), the actual value in the version column is != 1.
It means that someone updated the entity (in the mean time between the READ and the transaction COMMIT) and so the values you have just read are no more valid at commit time.

JPA getSingleResult() did not receive any entities error

I'm writing updates as part of CRUD testing and when I test my code, I get an error saying no entities are found. I have no idea why, because my partner did the exact same code and he worked perfectly. Neither of us is able to figure out what's going on. I'm getting an error on the getSingleResult() method.
#Test
public void updateBookTest() {
Book book = em.createQuery("select b from Book b where b.title = :title", Book.class).setParameter("title", "createABook").getSingleResult();
tx.begin();
book.setTitle("updatedThisBook");
book.setAuthor("newAuthor");
tx.commit();
Book updatedBook = em.find(Book.class, book.getBookId());
assertEquals(book.getTitle(), updatedBook.getTitle());
assertEquals(book.getAuthor(), updatedBook.getAuthor());
System.out.println("updateBookTest:\t" + book.toString());
tx.begin();
book.setTitle("createABook");
tx.commit();
}
This is my code. Let me know if more information is needed.
getSingleResult must to throw a NoResultException if there is no result.
So, your test is Ok.
Check that both are using same database (and there is no data returned), both are running same query, and both are using same jpa implementation versions.
From javadoc (since jpa 1.0):
getSingleResult
java.lang.Object getSingleResult(): Execute a SELECT query that returns a single untyped result.
Returns: the result
Throws:
NoResultException - if there is no result
NonUniqueResultException - if more than one result
IllegalStateException - if called for a Java Persistence query language UPDATE or DELETE statement
QueryTimeoutException - if the query execution exceeds the query timeout value set and only the statement is rolled back
TransactionRequiredException - if a lock mode has been set and there is no transaction
PessimisticLockException - if pessimistic locking fails and the transaction is rolled back
LockTimeoutException - if pessimistic locking fails and only the statement is rolled back
PersistenceException - if the query execution exceeds the query timeout value set and the transaction is rolled back
Reference javadoc getSingleResult
Another point, check that your friend is not calling getResultList instead of getSingleResult. This method returns a list and no throw an exception if empty.
Reference javadoc getResultList