I'm building a web application using SpringFramework. In the data access layer I was using Hibernate to query data in MySQL, which was working fine. My SessionFactory is build from this method:
public SessionFactory sessionFactory() throws HibernateException {
return new org.hibernate.cfg.Configuration()
.configure()
.buildSessionFactory();
}
Today I'm going to integrate my data access with JPA, which needs a EntityManagerFactory, IMO I only need to change the code above into the following:
#Bean
public EntityManagerFactory entityManagerFactory() throws HibernateException {
return new org.hibernate.cfg.Configuration()
.configure()
.buildSessionFactory();
}
simply because SessionFactory extends EntityManagerFactory. However I got an exception
java.lang.ClassCastException:
org.hibernate.internal.SessionFactoryImpl cannot be cast to
javax.persistence.EntityManagerFactory
This is quite weird, because SessionFactoryImpl implements SessionFactory while SessionFactory extends EntityManagerFactory. I don't know why the cast fails.
My question is: 1. why the cast is invalid? 2. what's the correct way to build a EntityManagerFactory using Hibernate?
EDIT Debugger says
factory instanceof SessionFactory //true
factory instanceof EntityManagerFactory //false
and the source of SessionFactory
public interface SessionFactory extends EntityManagerFactory, HibernateEntityManagerFactory, Referenceable, Serializable, Closeable
I'm sure all the above EntityManagerFactory refers to javax.persistence.EntityManagerFactory.
Hmmm, unless you're using Hibernate 5.2 Hibernate's org.hibernate.SessionFactory does not extend javax.persistence.EntityManagerFactory, hibernate-entitymanager was only recently merged into hibernate-core. Seems to me that you're browsing source code from a version newer than the one you're using in your project.
In either case, to see the correct way of using Hibernate with JPA, refer to this link.
You can reuse the Session factory by following the steps here, setting up the SessionFactory bean in Spring.
But since you've already coded in terms of the EntityManagerFactory, you should follow the easier route described in this tutorial It's in German, unfortunately, but the code should speak for itself. You declare Hibernate as your JPA vendor, then the EntityManagerFactory with the vendor and the datasource, which can be defined explicitly or be a JNDI datasource, defined in your container and requested like this:
<jee:jndi-lookup id="dataSource" jndi-name="myDataSource"/>
And then it's very nice to declare a Spring transactionManager to allow the #Transactional annotation so you won't have to deal with explicit transaction management.
Then you can let your UserDAO look like this:
public class UserDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public List<User> findUserByName(String name) {
return em.createNamedQuery("User.findByName")
.setParameter("name", name)
.getResultList();
}
}
where User is something like
#Entity
#NamedQueries({ #NamedQuery( name="User.findByName", query="select u from User where u.name = :name") })
public class User {
#Id
private Long id;
#Column
private String name;
...
}
There's many ways to do this, though.
Related
In Spring Boot application with HikariCP dataSource I've execute HQL queries with helper class:
public class QueryExecutor {
private Session session;
#Autowired
private SessionFactory sessionFactory;
public QueryExecutor getConnection() {
session = sessionFactory.openSession();
session.beginTransaction();
return this;
}
public void closeConnection() {
session.getTransaction().commit();
session.close();
}
public List execute(String hql, Long limit, Long offset) {
getConnection();
Query query = session.createQuery(hql);
List list = query.list();
closeConnection();
return list;
}
It works ok, but when I start using class widely, application starts freezes because Hibernate Session is closed randomly and transaction wait 30 seconds (default timeout value for HikariCP transactions) before get Session is closed error (or currentPersistenceContext is null if I used getCurrentSession() instead openSesssion()).
First of all, I changed open session to getCurrentSession function. But I also need to specify context with #PersistenceContext or hibernate.current_session_context_class=thread in hibernate.cfg.xml.
I read that this property better to use with default value.
Also I specify hibernate.connection.release_mode=AFTER_TRANSACTION.
But that isn't solve a problem.
After all, I changed class like that:
#PersistenceContext
private EntityManager entityManager;
#Transactional
public List execute(String hql, Long limit, Long offset) {
Query query = entityManager.createQuery(hql);
return query.getResultList();
}
and use javax.persistence.Query instead Hibernate queries.
Now it works ok. But is that a correct modifications?
All functions worked with execute method of QueryExecutor annotated with #Transactional. As I uderstood, in that case no beginTransaction() needed.
But need I close entityManager after execute() ?
SessionFactory used with Hibernate without JPA and EntityManager used with Hibernate JPA technologies?
How can I solve problem without using EntityManager?
You don't need to close transactions manually if you use #Transactional annotation.
But if you use it, I will reccomend you try to use JPA Repositories and wrap in #Transactional annotation the methods of business logic only.
In which case you will no longer need EntityManager and you will be able to create custom complex queries with JpaSpecificationExecutor and JPA Criteria API Queries.
Iam using Spring's #Transactional annotation for my DAO classes/methods for DB operations.
Hibernate ORM configured with PostgreSQL
SessionFactory is created with #Bean in configuration and retreived using #Autowired in DAO classes.
#Autowired
SessionFactory sessionFactory;
#Transactional(value = "myTransactionManager")
public int getTotalClientsInDatabase() {
Query countTotalClientsQuery = sessionFactory.getCurrentSession()
.getNamedQuery(DeviceInfo.COUNT_TOTAL_CLIENTS);
return Integer.parseInt(countTotalClientsQuery.uniqueResult().toString());
}
Whenever getTotalClientsInDatabase is called, a Transaction is opened always.
I want to prevent opening of a Transaction as it is a SELECT(DDL) query.
If #Transactional is removed Exception is thrown saying sessionFactory is not synchronized.
If readOnly=true is added performance is reduced.
#Transactional(value = "myTransactionManager", readOnly=true)
Is there any way to stop Session from opening a Transaction??
Good practice would be in your case to mark the transaction with a propagation level of SUPPORTS:
Support a current transaction, execute non-transactionally if none
exists.
So if the parent method is not in a transactional context, this method will also not be, but can.
#Transactional(propagation=Propagation.SUPPORTS, value = "myTransactionManager")
My architecture includes creating the SessionFactory bean in the application-context.xml:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
</bean>
Then I have the base Repository class injecting this sessionFactory.
public class GenericRepositoryImpl<T extends BaseEntity> {
#Autowired
private SessionFactory sessionFactory;
The injection allows me to work with Named Queries, for example
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
protected List<T> findByNamedQuery(String queryString) {
// return entityManager.createNamedQuery(queryString).getResultList();
return sessionFactory.openSession().getNamedQuery(queryString).list();
}
I am using Hibernate 5 and Spring 4. How to add to this architecture the capability to use JPA 2 Criteria queries?
Did you try to use the Criteria inside your openSession provided by the injected SessionFactory? I'm not sure if it's possible to do in that version:
Criteria = sessionFactory.openSession().createCriteria(Foo.class);
Inject Entitymanager directly to your GenericRepository as below.
#PersistenceContext
protected EntityManager entityManager;
and start creating criteria
entityManager.getCriteriaBuilder()
I am seeing two classes:
/**
* This class uses CDI to alias Java EE resources, such as the persistence context, to CDI beans
*
*/
public class Resources {
#Produces
#PersistenceContext
private EntityManager em;
#Produces
public Logger produceLog(InjectionPoint injectionPoint) {
return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
#Produces
#RequestScoped
public FacesContext produceFacesContext() {
return FacesContext.getCurrentInstance();
}
}
and
// The #Stateless annotation eliminates the need for manual transaction demarcation
#Stateless
public class MemberRegistration {
#Inject
private Logger log;
#Inject
private EntityManager em;
#Inject
private Event<Member> memberEventSrc;
public void register(Member member) throws Exception {
log.info("Registering " + member.getName());
em.persist(member);
memberEventSrc.fire(member);
}
}
I have 2 questions on this:
1) MemberRegistration can inject "log" and "em" directly, is that because the Resources already define them by using #Produces annotation? Without the Resources class, would the MemberRegistration class still work? I am trying to understand whether or how the two classes are related, and how the CDI works.
2) In MemberRegistration's register method, there is only a "em.persist()" method used. A complete flow of using EntityManager looks like the following. In the example application, I didn't see methods "commit()" and "close()" are used. So how the transaction is committed and closed?
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.persist( SomeObject );
entityManager.getTransaction().commit();
entityManager.close();
Answering your questions:
1) MemberRegistration can inject "log" and "em" directly, is that because the Resources already define them by using #Produces annotation?
Yes. #Inject will work only for types that are known to CDI (are discovered via class-path scanning or declared manually via #Produces). So without your Resources class, which defines EntityManager and Logger as a CDI managed beans, injection via #Inject would not work.
BTW. For details you can read cdi-spec-1.2 - PDF version is 170 pages long, not that big, but also not so short.
2) So how the transaction is committed and closed?
... you even have a valid comment in your code: the #Stateless annotation eliminates the need for manual transaction demarcation. For details read something about CMT transactions in EJB.
Honestly, I agree with #JBNizet. It is disappointing to see you asking such questions (especially the first one) which can be answered immediately by yourself with just a quick test.
My problem is actually simple but I really do not find a good solution to it.
I've currently to manage several DB in my application:
one UNIQUE admin DB (with a static name);
one client DB, with a name depending on the client.
I'm using JPA and I would like to create dynamically EntityManagers for client DB. But when I create this programmatically, I get this error:
javax.persistence.TransactionRequiredException: joinTransaction has been called on a resource-local EntityManager which is unable to register for a JTA transaction.
Here is the code:
#Stateful
public class ServiceImpl implements Service{
private EntityManagerFactory emf;
private EntityManager em;
#PostConstruct // automatically called when EJB constructed and session starts
public void init() {
emf = Persistence.createEntityManagerFactory("punit");
em = emf.createEntityManager();
}
...
And
#Stateful(mappedName = "CustomerService")
public class CustomerServiceImpl extends ServiceImpl implements CustomerService {
#Override
public void create(Customer cust) {
getEm().joinTransaction();
getEm().persist(cust);
}
More generally, I'v got problems with JPA. I just would like to connect to two databases, do some CRUD operations on them. But I really don't know how to manage transactions (my first approach was to let the container manage it...).
If someone could help me, could be great!
NB: I'm using a Glassfish Java EE server and PGSql DBs.
In jpa, you can declare several prsistenceunits in the persistence.xml file.
At the point of injection, you can do something like this:
#PersistenceContext(unitName = "unitName0", properties={#PersistenceProperty(...)}
EntityManager emClient;
#PersistenceContext(unitName = "unitName1", properties={#PersistenceProperty(...)}
EntityManager emAdmin;
This way, you dont have to create the entity managers manually, hence you get the container transaction management.
NOT TESTED:
If you have dynamic database names, you would inject EntityManagerFactory
#PersistenceContext(unitName ="name")
EntityManagerFactory emf;
//at the point you want the EntityManager
Map<String, String> props; //put the connection property for the EM here
EntityManager em = emf.createEntityManager(props);
Based on the fact that in a J2EE environment we use the concept of DataSources, and ConnectionPooling, it would be nearly impossible to implement this kind of dynamic datasources, without resorting to manual creation of entitymanagerfactory.
This is my reasoning:
The server manages the connection pooling, and the jpa provider (such as eclipselink) uses jndi to determine the connection to the database. This implies that if you were to change the database name, then it must also have a connection pooling resources, and an associated jdbc resource. This will ofcourse negate what you want to do.
Basic solution: Create EntityManagerFactory manually and manually manage transactions.
Specify in the persistence xml that the unit is non-jta for this to work.
Then you can programmatically supply connection data based on user-session:
Something of this sort:
//this must be session specific.
class PersistenceSession{
static Map<String, String> clientSessionProps;
//When new session starts and a new client has logged in.
static void setClientConnectionProperties(Client client){
.....
}
static Map<String, String> getClientSessionProps(){
return clientSessionProps;
}
}
At the ejb level.
#Stateless
public class TestEntityFacade extends AbstractFacade<TestEntity> {
private EntityManagerFactory emf;
#PostConstruct
void init(){
emf = Persistence.createEntityManagerFactory("name");
}
#Override
protected EntityManager getEntityManager() {
return emf.createEntityManager(PersistenceSession.getClientSessionProps());
}
public TestEntityFacade() {
super(TestEntity.class);
}
void add(Entity e){
EntityManager em = getEntityManager();
em.getTransaction().begin();
.....
em.getTransaction().commit();
}
}
Actually very neat way how to do this is with CDI Producers, you can define a bean which will produce for your any number of custom Entity Managers, see the example.
#SessionScoped
public class EntityManagerProducer {
#Produces
#AdminDB
#PersistenceContext(unitName = "adminDB")
public EntityManaged adminDB;
#Produces
#UserDB
#PersistenceContext(unitName = "userDB")
public EntityManaged userDB;
}
This means that these two EntityManagers get created within every user session and than you can inject them to any bean with
#Inject
#UserDB
private EntityManager em; //if you want to use UserDB now
#UserDB and #AdminDB are your own defined Qualifiers. This leads to much more easier and readable code.