In the SDN 4.1 reference, the configuration includes extending Neo4jConfiguration and setting up a Session object explicitly as a #Bean. In 4.2, the guidance is to not extend Neo4jConfiguration and follow the config below. Note that explicitly setting up a standalone Session object is absent:
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
config
.driverConfiguration()
.setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver")
.setURI(dbLocation);
return config;
#Bean
public SessionFactory sessionFactory() {
SessionFactory sessionFactory = new SessionFactory(configuration(), "org.my.package" );
sessionFactory.register(new MyPreSaveListener());
return sessionFactory;
}
I've seen this config used while #Autowiring the Session object itself (not the factory) in repository classes. Does this mean that there will then be only one Session instance across the application? If so, doesn't that go against the idea that a Session lifespan should be limited to an application's "unit of work?"
Note that my repositories are custom and do not extend the neo4j repos, as I am currently migrating away from using the Neo4jTemplate object.
No, there is not one session per application. So long as your Session is called from within a #Transactional annotation or TransactionTemplate it will call a proxy Session (which is generated by the Spring Data Neo4j framework during startup). This will effectively create a session for the lifetime of the transaction (where it is demarcated) and once out of scope, allow it to be garbage collected.
I have a runnable task as follows
#TransactionManagement(TransactionManagementType.BEAN)
#TransactionAttribute(TransactionAttributeType.NEVER)
public class Task implements Runnable {
private SessionFactory sessionFactory;
public Task(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void run() {
StatelessSession session = sessionFactory.openStatelessSession();
session.beginTransaction();
// ... the rest is irelevant because beginTransaction launches an exception
SessionFactory is supplied by a bean but when I try to begin the transaction, even tough I marked the class as TransactionAttributeType.NEVER, hibernate launches the exception
org.hibernate.TransactionException: Could not register synchronization for container transaction
I tried this but I only got another problem
org.hibernate.TransactionException: Could not find UserTransaction in JNDI [java:comp/UserTransaction]
Stateless session do not require transactions, remove the transaction statements ant try. You can read up more here
A stateless session does not implement a first-level cache nor
interact with any second-level cache, nor does it implement
transactional write-behind or automatic dirty checking, nor do
operations cascade to associated instances.
I am learning Hibernate now and I need help to understand how Sessions work. I have some methods in a class which I have given below.
I see there is a getCurrentSession() in SessionFactory class. So, it seems that only one Session can be "active" inside a SessionFactory. Is this SessionFactory like
a queue of transactions where the transactions are completed in in order ? If yes, then
is it possible to promote a transaction to a higher or lower priority ?
private static SessionFactory factory;
//Get a hibernate session.
public static Session getSession(){
if(factory == null){
Configuration config = HibernateUtil.getConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession = factory.getCurrentSession();
return hibernateSession;
}
public static void commitTransaction(){
HibernateUtil.getSession().getTransaction().commit();
}
public static void rollbackTransaction(){
HibernateUtil.getSession().getTransaction().rollback();
}
And some more methods that use getTransaction().
SessionFactory's job is to hide the session creation strategy. For example, in a web application, you probably want the SessionFactory to return create a Session the first time getCurrentSession() is called on a thread, and then return the same Session from that point forward for the duration of the request. (Since you probably want to load customer data from that session, then maybe modify their account in that same session.) Other times, you may want SessionFactory to create a brand new session every time you call getCurrentSession(). So by hiding this decision behind the SessionFactory API, you simply write code that gets the Session from the factory and operates on it.
The Session is what handles transactions. As you probably expect, transactions are started in a Session, and then either complete or rollback. There is really no way to prioritize them since once they are started, you are committed to either rolling it back or committing it.
I am new to Hibernate and I'm not sure whether to use a Hibernate SessionFactory or a JPA EntityManagerFactory to create a Hibernate Session.
What is the difference between these two? What are the pros & cons of using each of those?
Prefer EntityManagerFactory and EntityManager. They are defined by the JPA standard.
SessionFactory and Session are hibernate-specific. The EntityManager invokes the hibernate session under the hood. And if you need some specific features that are not available in the EntityManager, you can obtain the session by calling:
Session session = entityManager.unwrap(Session.class);
SessionFactory vs. EntityManagerFactory
As I explained in the Hibernate User Guide, the Hibernate SessionFactory extends the JPA EntityManagerFactory, as illustrated by the following diagram:
So, the SessionFactory is also a JPA EntityManagerFactory.
Both the SessionFactory and the EntityManagerFactory contain the entity mapping metadata and allow you to create a Hibernate Session or a EntityManager.
Session vs. EntityManager
Just like the SessionFactory and EntityManagerFactory, the Hibernate Session extends the JPA EntityManager. So, all methods defined by the EntityManager are available in the Hibernate Session.
The Session and the `EntityManager translate entity state transitions into SQL statements, like SELECT, INSERT, UPDATE, and DELETE.
Hibernate vs. JPA bootstrap
When bootstrapping a JPA or Hibernate application, you have two choices:
You can bootstrap via the Hibernate native mechanism, and create a SessionFactory via the BootstrapServiceRegistryBuilder. If you're using Spring, the Hibernate bootstrap is done via the LocalSessionFactoryBean, as illustrated by this GitHub example.
Or, you can create a JPA EntityManagerFactory via the Persistence class or the EntityManagerFactoryBuilder. If you're using Spring, the JPA bootstrap is done via the LocalContainerEntityManagerFactoryBean, as illustrated by this GitHub example.
Bootstrapping via JPA is to be preferred. That's because the JPA FlushModeType.AUTO is a much better choice than the legacy FlushMode.AUTO, which breaks read-your-writes consistency for native SQL queries.
Unwrapping JPA to Hibernate
Also, if you bootstrap via JPA, and you have injected the EntityManagerFactory via the #PersistenceUnit annotation:
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
You can easily get access to the underlying Sessionfactory using the unwrap method:
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
The same can be done with the JPA EntityManager. If you inject the EntityManager via the #PersistenceContext annotation:
#PersistenceContext
private EntityManager entityManager;
You can easily get access to the underlying Session using the unwrap method:
Session session = entityManager.unwrap(Session.class);
Conclusion
So, you should bootstrap via JPA, use the EntityManagerFactory and EntityManager, and only unwrap those to their associated Hibernate interfaces when you want to get access to some Hibernate-specific methods that are not available in JPA, like fetching the entity via its natural identifier.
I want to add on this that you can also get Hibernate's session by calling getDelegate() method from EntityManager.
ex:
Session session = (Session) entityManager.getDelegate();
I prefer the JPA2 EntityManager API over SessionFactory, because it feels more modern. One simple example:
JPA:
#PersistenceContext
EntityManager entityManager;
public List<MyEntity> findSomeApples() {
return entityManager
.createQuery("from MyEntity where apples=7", MyEntity.class)
.getResultList();
}
SessionFactory:
#Autowired
SessionFactory sessionFactory;
public List<MyEntity> findSomeApples() {
Session session = sessionFactory.getCurrentSession();
List<?> result = session.createQuery("from MyEntity where apples=7")
.list();
#SuppressWarnings("unchecked")
List<MyEntity> resultCasted = (List<MyEntity>) result;
return resultCasted;
}
I think it's clear that the first one looks cleaner and is also easier to test because EntityManager can be easily mocked.
Using EntityManagerFactory approach allows us to use callback method annotations like #PrePersist, #PostPersist,#PreUpdate with no extra configuration.
Using similar callbacks while using SessionFactory will require extra efforts.
Related Hibernate docs can be found here and here.
Related SOF Question and Spring Forum discussion
By using EntityManager, code is no longer tightly coupled with hibernate. But for this, in usage we should use :
javax.persistence.EntityManager
instead of
org.hibernate.ejb.HibernateEntityManager
Similarly, for EntityManagerFactory, use javax interface. That way, the code is loosely coupled. If there is a better JPA 2 implementation than hibernate, switching would be easy. In extreme case, we could type cast to HibernateEntityManager.
EntityManagerFactory is the standard implementation, it is the same across all the implementations. If you migrate your ORM for any other provider like EclipseLink, there will not be any change in the approach for handling the transaction. In contrast, if you use hibernate’s session factory, it is tied to hibernate APIs and cannot migrate to new vendor.
EntityManager interface is similar to sessionFactory in hibernate.
EntityManager under javax.persistance package but session and sessionFactory under org.hibernate.Session/sessionFactory package.
Entity manager is JPA specific and session/sessionFactory are hibernate specific.
I have to do some database stuff in my repository' #PostConstruct:
#Repository
public class SomeRepositoryHibernate implements SomeRepository {
private SessionFactory sessionFactory;
#Autowired
public SomeRepositoryHibernate(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
...
#PostConstruct
public void doSomestuffWithDb() {
...
}
}
but it fails with:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and
configuration does not allow creation of non-transactional one here
is there any easy solution for that?
Thanks!
You don't have a running transaction in #PostConstruct
you can't use #Transactional there (except for mode="aspectj"), so spring can't start the transaction
a transaction is required for hibernate to mutate data (insert/update/delete)
So, you would have to create your session from the session factory (via .openSession()) there and start a transaction manually.
assuming you are using hibernate combining with spring and you have configured your sessionFactory and transaction manager correctly in your spring config file.
Then the root cause is that when your doSomestuffWithDb() method is invoked the transaction prepare work has not been finished by spring. The #postconstruct can only ensure the method is called after the bean is created, it can not ensure the container is ready for everything- here, I mean the transaction related stuff- at the moment.
There is a detailed discussion in spring forum.
http://forum.springsource.org/showthread.php?58337-No-transaction-in-transactional-service-called-from-PostConstruct
Also, the author submitted his solution to jira at
https://jira.springsource.org/browse/SPR-5966?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel#issue-tabs