Configure Spring transactions or EJB CMT instead - java

I tried to configure Spring Boot with Hibernate:
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.datalis.plugin.database.dao.TerminalsService;
import org.datalis.plugin.database.models.TerminalsModel;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
#Qualifier("terminalsService")
public class TerminalsDaoHibernate implements TerminalsService {
#Autowired
private EntityManager entityManager;
#Override
#Transactional
public TerminalsModel getTerminalToken(String terminalToken) throws Exception {
TerminalsModel terminal = null;
Session session = entityManager.unwrap(Session.class);
try {
entityManager.getTransaction().begin();
terminal = (TerminalsModel) session.get(TerminalsModel.class, terminalToken);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
throw new Exception("Error");
}
return terminal;
}
}
But I get this error:
14:47:34,323 ERROR [stderr] (default task-1) java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
14:47:34,323 ERROR [stderr] (default task-1) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:255)
What is the proper way to configure #Transactional properly?
Do I need to use Transaction is a different way?

You are using #Transactional and still are trying to manually start a transaction. Either do manual transaction management (i.e remove the #Transactional) or embrace #Transactional by removing the manual transaction management code.
#Override
#Transactional
public TerminalsModel getTerminalToken(String terminalToken) throws Exception {
TerminalsModel terminal = null;
Session session = entityManager.unwrap(Session.class);
return (TerminalsModel) session.get(TerminalsModel.class, terminalToken);
}
However I don't see why you would want to use plain Hibernate over JPA here. The same result can be achieved by using JPA.
#Override
#Transactional
public TerminalsModel getTerminalToken(String terminalToken) throws Exception {
return entityManager.find(TerminalsModel.class, terminalToken);
}
Generally there is no need to use the plain Hibernate API over JPA with the current state of the JPA API.

Related

HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)

I have the following error and I am trying to figure out the reason. As far as I know, this is not caused by Hibernate but the way I manage/use the method calls (Transient entity instance, save, detach and then merge). I do not get why this problem happens. I would really appreciate your explanation.
Error:
HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: possible non-threadsafe access to session
FYI:
Java Version: 1.8
Hibernate version: https://hibernate.org/orm/releases/5.5/
package lk.mysite.demo.orm;
import lk.mysite.demo.orm.entity.Customer;
import lk.mysite.demo.orm.util.HibernateUtil;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class ContextDemo16 {
public static void main(String[] args) {
try (SessionFactory sf = HibernateUtil.getSessionFactory();
Session session = sf.openSession();) {
session.beginTransaction();
Customer customer = new Customer(5, "John", "New York");
session.save(customer);
session.detach(customer);
session.merge(customer);
// prompt an error
session.getTransaction().commit();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}

EntityManager em.getTransaction().begin() locks thread

My question is, if the method "begin()" is capable of lock a thread further than the "timeout" config in the persistence.xml.
Here is a snippet:
#Inject EntityManager em;
#Inject ContextControl ctxCtrl;
String fileType;
String fileName;
String hash;
BufferedReader reader = null;
public void run(File f, String fileType, String hash) throws ProcessorException, IOException{
this.fileType = fileType;
this.hash= hash;
this.fileName = f.getName();
try {
ctxCtrl.startContext(RequestScoped.class);
em.getTransaction().begin();
reader = openReader(f);
//rest of the code...
em.getTransaction().commit();
}catch (Exception e) {
logger.error(e.getMessage(), e);
try{ //for database breakdown purpose
em.getTransaction().rollback();
}catch(Exception e2){
logger.error(e2.getMessage(), e2);
throw new ProcessorException();
}
throw new ProcessorException();
}finally{
reader.close();
ctxCtrl.stopContext(RequestScoped.class);
}
"run" method is executed inside a loop. This method is executed serially, there is no possible concurrency.
Now, the thing is that the thread stops randomly at line "em.getTransaction().begin();", with no exception. And since this is a critical area, all the application is stopped and the lock is never released.
The only thing I can think of is the "begin()" method getting stuck somehow, but not in an exception way, but rather in a lock way (since no exception is caught).
I wasn't able to recreate the issue, I can only say that the issue has nothing to do with the file. Also, this is happening in production, so I can't debug the application other than check some logs
thanks in advance
EDIT
I use Deltaspike to provide the CDI. Entitymanager it's injected anytime it's needed. It's created like this:
CLASS ENTITYMANAGER FACTORY PRODUCER
import java.io.FileInputStream;
import java.util.Properties;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.slf4j.Logger;
#ApplicationScoped
public class EntityManagerFactoryProducer {
#Inject Logger logger;
#Produces
#ApplicationScoped
public EntityManagerFactory create() {
Properties props = new Properties();
try {
props.load(new FileInputStream("cfg/connection.properties"));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return Persistence.createEntityManagerFactory("scgach",props);
}
public void destroy(#Disposes EntityManagerFactory factory) {
factory.close();
}
}
CLASS ENTITYMANAGER PRODUCER
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
#ApplicationScoped
public class EntityManagerProducer {
#Inject EntityManagerFactory emf;
#Produces #RequestScoped
public EntityManager create() {
return emf.createEntityManager();
}
public void destroy(#Disposes EntityManager em) {
if(em.isOpen())
em.close();
}
}

Injecting hibernate session to Jersey using HK2

I'm developing little app and have some issues with DI.
I have a repository class for persisting my entities which I inject to my service. And I'd like to inject Session object to it using H2K.
For this purpose I try do something similar described in following SO posts:
Jersey + HK2 + Grizzly: Proper way to inject EntityManager?
Using Jersey 2.0, how do you register a bindable instance per request?
How do I properly configure an EntityManager in a jersey / hk2 application?
So I created SFFactory class and register it in ApplicationConfig.
SFFactory.java
import org.glassfish.hk2.api.Factory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class SFFactory implements Factory<Session> {
private SessionFactory factory;
public SFFactory() {
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
StandardServiceRegistryBuilder srBuilder = new StandardServiceRegistryBuilder();
srBuilder.applySettings(configuration.getProperties());
factory = configuration.buildSessionFactory(srBuilder.build());
}
#Override
public Session provide() {
return factory.openSession();
}
#Override
public void dispose(Session session) {
if (session.isOpen()) {
session.close();
}
}
}
ApplicationConfig.java
import org.alexdzot.phonettesttask.repository.MessageRepository;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.hibernate.Session;
import javax.inject.Singleton;
import javax.ws.rs.ApplicationPath;
#ApplicationPath("/rest/*")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("org.alexdzot.phonettesttask");
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(SFFactory.class).to(Session.class);
bindFactory(MessageRepositoryFactory.class).to(MessageRepository.class).in(Singleton.class);
}
});
}
}
DefaultMessageRepository
import org.alexdzot.phonettesttask.model.Message;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.jvnet.hk2.annotations.Service;
import javax.inject.Inject;
import java.util.List;
#Service
public class DefaultMessageRepository implements MessageRepository {
#Inject
private Session session;
public void saveMessage(Message message) {
Session session = null;
Transaction tx = null;
try {
tx = session.beginTransaction();
session.save(message);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
session.close();
}
}
public List<Message> getAllMessages() {
Session session = null;
Transaction tx = null;
List<Message> messages = null;
try {
tx = session.beginTransaction();
messages = session.createCriteria(Message.class).list();
} catch (Exception e) {
tx.rollback();
} finally {
session.close();
return messages;
}
}
}
But when I run the app and trying to call repository methods I get NullPoinerException. I can't understand what am I doing wrong.
This is what Tomcat log is saying:
*08-Aug-2015 02:42:18.232 SEVERE [http-nio-8080-exec-10] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [org.alexdzot.phonettesttask.config.ApplicationConfig] in context with path [] threw exception [java.lang.NullPointerException] with root cause
java.lang.NullPointerException
at org.alexdzot.phonettesttask.repository.DefaultMessageRepository.getAllMessages(DefaultMessageRepository.java:42)
at org.alexdzot.phonettesttask.service.MessageResource.viewSentMessages(MessageResource.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:205)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:308)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:291)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1140)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:403)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:386)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:334)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:221)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)*
Any ideas what wrong with my solution and what causes that problem?
Main problem:
[java.lang.NullPointerException]
Look at your repository class
#Service
public class DefaultMessageRepository implements MessageRepository {
#Inject
private Session session; <-------------------+
| // You shadow session
public void saveMessage(Message message) { |
Session session = null; <--------------+
Transaction tx = null;
try {
tx = session.beginTransaction();
...
}
...
}
You are shadowing the injected field session. So you are just using the local session, which is null. So in your method, just get rid of Session session = null;. The injection should work fine, and you should be able to just use the session field.
Another Problem:
The same Session is being used for all request. Since DefaultMessagFactory is a singleton, HK2 asserts that Session should also be a singleton, since it is contained within a singleton. So instead of making the SFFactory the default #PerLookup scope, you get a factory that is a singleton, and so only one Session will be created. This is not the behavior you should want. You can test this by putting some print statements inside your factory class.
One way to make sure that a new Session is created for each request is to:
Use javax.inject.Provider<Session>, to lazily load the session, which will allow use to keep it in the scope we configured.
#Inject
private javax.inject.Provider<Session> session;
#Override
public void saveEvent(Event event) {
Session s = session.get();
Transaction tx = s.beginTransaction();
Configure the SFFactory in a request scope
#Override
protected void configure() {
bindFactory(SFFactory.class)
.to(Session.class)
.in(RequestScoped.class);
}
The other thing to fix is that SessionFactory should be a single instance through the application. For that we can create a single factory for it, and inject it into the SFFactory. For example
public class SessionFactoryFactory implements Factory<SessionFactory> {
private final SessionFactory factory;
public SessionFactoryFactory() {
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
StandardServiceRegistryBuilder srBuilder = new StandardServiceRegistryBuilder();
srBuilder.applySettings(configuration.getProperties());
factory = configuration.buildSessionFactory(srBuilder.build());
System.out.println("--- SessionFactory Created ---");
}
#Override
public SessionFactory provide() {
System.out.println("--- SessionFactory Provided ---");
return factory;
}
#Override
public void dispose(SessionFactory factory) {
factory.close();
}
}
The in your SFFactory
public class SFFactory implements Factory<Session> {
private final SessionFactory factory;
#Inject
public SFFactory(SessionFactory factory) {
this.factory = factory;
}
#Override
public Session provide() {
System.out.println("--- Session Created ---");
return factory.openSession();
}
#Override
public void dispose(Session session) {
if (session.isOpen()) {
session.close();
}
}
}
You configuration would change to look like
#Override
protected void configure() {
bindFactory(SessionFactoryFactory.class)
.to(SessionFactory.class)
.in(Singleton.class);
bindFactory(SFFactory.class)
.to(Session.class)
.in(RequestScoped.class);
}
UPDATE
So it seems aside from the shadowing, another main problem was in the MessageRepositoryFactory that the OP didn't show (in github project from comments).
public class MessageRepositoryFactory implements Factory<MessageRepository> {
#Override
public MessageRepository provider() {
return new DefaultMessageRepository();
}
...
}
When you instantiate the object your self, by default, the framework doesn't handle the injection. So that's why the injection of the Session didn't happen.
Instead of using the a Factory like this, you could simply bind like this
bind(DefaultMessageRepository.class).to(MessageRepository.class).in(..);
This way the framework creates the instance. The Factory should really only be used where it is not possible to bind the above way, for instance you have some initializations to do. We could have even not created the Factory for the SessionFactoryFactory and instead did all the session factory configuration elsewhere and just binded the instance. For example
SessionFactory sessionFactory =...
...
bind(sessionFactory).to(SessionFactory.class);
We just bind a SessionFactory singleton.

Disabling implicit transactions in Hibernate

I'm new to hibernate and I've written the following code,
package main;
import java.util.List;
import model.Message;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.stat.Statistics;
public class Main {
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration().configure();
SessionFactory factory = configuration.buildSessionFactory();
Session session = factory.openSession();
//Transaction transaction = session.beginTransaction();
List<Message> messages = session.createQuery("from Message").list();
Message message = new Message();
message.setMessage("Hello World");
session.save(message);
System.out.println(messages);
System.out.println(session);
session.close();
factory.close();
System.out.println("After the closure");
}
}
Here I've not begun the transaction and I've closed the session. But Hibernate didn't complain about Transactions. Are there implicit transactions, if so how should I disable them.
Hibernate didn't had a reason to complain about Transactions. :)
You closed the session after you did the work. You shouldn't create a new sessionFactory for each database edit though:
A SessionFactory is an expensive-to-create, threadsafe object,
intended to be shared by all application threads. It is created once,
usually on application startup, from a Configuration instance.
A Session is an inexpensive, non-threadsafe object that should be used
once and then discarded for: a single request, a conversation or a
single unit of work. A Session will not obtain a JDBC Connection, or a
Datasource, unless it is needed. It will not consume any resources
until used.
Read more about Hibernate transaction management here.

Transaction rollback in stateless session EJB 3.0

I have a stateless session EJB as per 3.0 spec.
/*Remote Interface*/
package com.nseit.ncfm2.data.ejb;
import java.sql.SQLException;
import java.util.Collection;
import javax.ejb.Remote;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.naming.NamingException;
import com.nseit.ncfm2.security.Audit;
#Remote
public interface ProductionDataChangesRequestsRemote {
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean shiftCandidateDetails(String sourceNcfmId,
String destinationNcfmId, Collection<String> specialCasesList, String shiftingRemarks, String user, Audit updtAudit) throws NamingException, SQLException;
}
/*Bean Class*/
package com.nseit.ncfm2.data.ejb;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.NamingException;
import com.nseit.ncfm2.security.Audit;
import com.nseit.ncfm2.util.server.lookup.LookUpServerResources;
import java.sql.*;
import java.util.*;
/**
* Session Bean implementation class ProductionDataChangesRequestsBean
*/
#Stateless(name = "ProductionDataChangesRequestsBean", mappedName = "ProductionDataChangesRequestsEJB")
#Remote(ProductionDataChangesRequestsRemote.class)
#TransactionManagement(TransactionManagementType.CONTAINER)
public class ProductionDataChangesRequestsBean implements
ProductionDataChangesRequestsRemote {
/**
* Default constructor.
*/
public ProductionDataChangesRequestsBean() {
// TODO Auto-generated constructor stub
}
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean shiftCandidateDetails(String sourceNcfmId,
String destinationNcfmId, Collection<String> specialCasesList,
String shiftingRemarks, String user, Audit updtAudit)
throws NamingException, SQLException {
// TODO Auto-generated method stub
Connection conn = null;
PreparedStatement pstmt = null;
int updtCnt = 0;
boolean areDetailsShifted = false;
try {
..............
..............
..............
/* Start: update table-1 */
..............
..............
..............
updtCnt = pstmt.executeUpdate();
..............
..............
..............
/* End: update table-1 */
/* Start: update table-2 */
..............
..............
..............
updtCnt = pstmt.executeUpdate();
..............
..............
..............
/* End: update table-2 */
areDetailsShifted = true;
} /*catch (SQLException e) {
// TODO Auto-generated catch block
System.out
.println("SQLException in ProductionDataChangesRequestsBean.shiftCandidateDetails(...) "
+ e.getMessage());
// e.printStackTrace();
context.setRollbackOnly();
} */finally {
LookUpServerResources.closeStatement(pstmt);
LookUpServerResources.closeConnection(conn);
}
return areDetailsShifted;
}
}
Currently, if the 1st table update succeeds and the 2nd table update gives an exception, a rollback is not taking place, i.e records in 1st table are updated.
I want the transaction to be rolled back in case an SQLException occurs (or for that matter, if any runtime exception occurs).
I tried two approaches :
Use of context.setRollbackOnly() in catch block for SQLException
Throwing the SQLException
In both the cases, the transaction didn't roll back.
How can I achieve this:
Without the usage of #ApplicationException annotation (as I do not have any application exceptions)
Without catching the SQLException and then calling context.setRollbackOnly()
Or what is the standard way?
You will have to throw RuntimeException
The standard way is to use underlying JPA for persistence rather then using JDBC.
JPA provides a standard OR mapping solution that's well-integrated into an EJB 3.x-compliant container.
Also from your code it reflects that you have TransactionManagementType.CONTAINER , but still managing transaction manually.
In bean-managed transaction
demarcation, the code in the session
or message-driven bean explicitly
marks the boundaries of the
transaction. Although beans with
container-managed transactions require
less coding, they have one limitation:
When a method is executing, it can be
associated with either a single
transaction or no transaction at all.
If this limitation will make coding
your bean difficult, you should
consider using bean-managed
transactions.
It looks like you are using JDBC API in your bean. I don't think container manages those JDBC transactions. For CMT, you'd have to invoke operations on a container managed entity manager for the rollback to work as expected.

Categories

Resources