The hibernate documentation says:
With CMT, transaction demarcation is declared in session bean deployment descriptors, rather than performed in a programmatic manner.
but I can't find any complete example on how to do this.
This is what I have in mind my code should look like:
#Stateless
public class Dao{
#Inject // or some other annotation
private SessionFactory factory;
public void doDaoStuff(){
Object obj = factory.getCurrentSession().get(Entity.class, "Id");
// do something with obj
return;
}
}
It is free from all the boilerplate that hibernate has as transaction should be started, committed and roll backed by container.
So, Is it possible to do this? Although the documentation says that required declarations should be specified in bean deployment descriptors, doing it with annotations would be great.
In JavaEE environments Hibernate can use the CMT (Container Managed Transaction) strategy which will bind hibernate transactions with the underlying JTA transaction eliminating the need to manually begin, commit and rollback transactions. Example here.
However, there are problems:
This doesn't work in all Java EE Containers. Newer versions of Websphere are not supported and quoting from the source code of hibernate - WebSphere, however, is not a sane JEE/JTA container...
This restricts to one session one transaction idiom. Thus during the invocation of a EJB business method, there can be only one JTA or Hibernate transaction.
Luckily, using CDI, and some custom interceptors this can be solved and a lot of Hibernate boilerplate can be removed. I wrote a sample on github.
This approach creates a wrapper for Hibernate SessionFactory and provides most commonly used APIs. Using CDI, #RequestScoped Sessions are opened and closed automatically. Transactions are managed using an Interceptor. #RequestScoped ensures one Session per request so sessions aren't shared among multiple requests.
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
#RequestScoped
public class MySessionFactory implements SessionFactoryTemplate{
#Inject
private SessionFactory sessionFactory;// Inject after creating the singleton instance
private Session currentSession;
public Session openSession(){
return sessionFactory.openSession();
}
public Session getCurrentSession(){
if(currentSession == null){
currentSession = sessionFactory.openSession();
}
return currentSession;
}
public StatelessSession openStatelessSession() {
return sessionFactory.openStatelessSession();
}
#PreDestroy
private void closeSession(){
if(currentSession!=null && currentSession.isOpen()) {
currentSession.close();
}
}
}
This implementation is then injected in database layer and used to obtain sessions.
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.ares.cdi.hibernate.interceptors.Transactional;
public class Dao {
#Inject
private MySessionFactory sf;
public void get(int id){
sf.getCurrentSession().get(clazz,id);
}
#Transactional
public void add(Object entity){
sf.getCurrentSesion().add(entity);
}
}
Transactions are managed by TranscationManager interceptor and declared by #Transactional annotation.
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;
#Interceptor
#Transactional
public class TransactionManager {
#Inject
private MySessionFactory sessionFactory;
#AroundInvoke
public Object handleTransaction(InvocationContext context) throws Exception{
Session session = sessionFactory.getCurrentSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
return context.proceed();
}
catch(Exception e){
tx.rollback();
throw e;
}
finally{
if(tx.getStatus().equals(TransactionStatus.ACTIVE)){
try{
tx.commit();
}
catch(Exception e){
tx.rollback();
throw e;
}
}
}
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Transactional {
}
EJB methods are transactional by default.
You can tweak their behavior using the TransactionAttribute annotation.
You can read more about CMT here:
https://docs.oracle.com/javaee/7/tutorial/transactions003.htm#BNCIJ
https://docs.oracle.com/javaee/7/tutorial/transactions.htm#BNCIH
Your Typical example would be like--
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
#TransactionManagement(TransactionManagementType.CONTAINER)
#Stateless(..)
public class YourBean{
#TransactionAttribute(TransactionAttributeType.REQUIRED) // if in case you wanted to use 'existing' transaction
public void DoStuff(){
}
}
And in your server configurations you require below tag under <enterprise-beans>
<transaction-type>Container</transaction-type>
Related
I have below Service:
#Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public void test(String idEntity) throws BaseException
{
getCustomerInformationDAO().updatetm(idEntity);
}
This service has been marked as #Service annotation.
I am calling this service from a controller.
#RequestMapping(value="/test", method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
#Override
public void test(#RequestParam("idEntity") String idEntity) throws BaseException
{
monolithicService.test(idEntity);
}
Below Dao(this has been marked as #Repository) method:
#Override
public void updatetm(String idEntity) throws BaseException
{
updateRecord( "customerinformation-update.updatelfcentitylDt", idEntity );
}
Transaction manager has been marked as
<tx:annotation-driven transaction-manager="transactionManager" />.
With above changes, it doesnt commit the transaction, even if it is successful.
Can anyone help me with this...
I dealt with a similar issue for a whole day.
Just when I was borderline with insanity, I found out that when you use #Transactional in a test the rules are different: by default, your changes are rolled back.
Quick solution: add the annotation #Commit to your method, i.e.:
#Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
#Commit
public void test(String idEntity) throws BaseException
{
getCustomerInformationDAO().updatetm(idEntity);
}
You can read some details in the following text:
One common issue in tests that access a real database is their effect on the state of the persistence store. Even when you use a development database, changes to the state may affect future tests. Also, many operations — such as inserting or modifying persistent data — cannot be performed (or verified) outside of a transaction.
The TestContext framework addresses this issue. By default, the framework creates and rolls back a transaction for each test. You can write code that can assume the existence of a transaction. If you call transactionally proxied objects in your tests, they behave correctly, according to their configured transactional semantics. In addition, if a test method deletes the contents of selected tables while running within the transaction managed for the test, the transaction rolls back by default, and the database returns to its state prior to execution of the test. Transactional support is provided to a test by using a PlatformTransactionManager bean defined in the test’s application context.
If you want a transaction to commit (unusual, but occasionally useful when you want a particular test to populate or modify the database), you can tell the TestContext framework to cause the transaction to commit instead of roll back by using the #Commit annotation.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testing
You probably have two data sources using MapperScan that might be messing up a mybatis config. You need to add SqlSessionFactory and SqlSessionTemplate as mentioned here http://mybatis.org/spring/getting-started.html.
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
#Configuration
#Slf4j
#MapperScan(value = "com.abc.xyx.aaa", sqlSessionTemplateRef = "PrimarySessionTemplate")
public class MyBatisPrimaryConfig {
#Bean(name = "PrimarySessionFactory")
#Primary
public SqlSessionFactory sessionFactory(#Qualifier("PrimaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
#Bean(name = "PrimarySessionTemplate")
#Primary
public SqlSessionTemplate primarySessionTemplate(#Qualifier("PrimarySessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
How to programmatically control transaction boundaries within single #Test method? Spring 4.x documentation has some clues but I think I missing something since the test throws error:
java.lang.IllegalStateException:
Cannot start a new transaction without ending the existing transaction first.
Test
import com.hibernate.query.performance.config.ApplicationConfig;
import com.hibernate.query.performance.config.CachingConfig;
import com.hibernate.query.performance.persistence.model.LanguageEntity;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.transaction.TestTransaction;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import java.util.List;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { ApplicationConfig.class, CachingConfig.class }, loader = AnnotationConfigContextLoader.class)
#PersistenceContext
#Transactional(transactionManager = "hibernateTransactionManager")
#TestExecutionListeners({})
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests {
private static Logger logger = LoggerFactory.getLogger(EHCacheTest.class);
#BeforeClass
public static void setUpBeforeClass() throws Exception {
logger.info("setUpBeforeClass()");
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
logger.info("tearDownAfterClass()");
}
#Autowired
private SessionFactory sessionFactory;
#Test
public void testTransactionCaching(){
TestTransaction.start();
Session session = sessionFactory.getCurrentSession();
System.out.println(session.get(LanguageEntity.class, 1));
Query query = session.createQuery("from LanguageEntity le where le.languageId < 10").setCacheable(true).setCacheRegion("language");
#SuppressWarnings("unchecked")
List<LanguageEntity> customerEntities = query.list();
System.out.println(customerEntities);
session.getTransaction().commit();
TestTransaction.flagForCommit();
TestTransaction.end();
// Second Transaction
TestTransaction.start();
Session sessionNew = sessionFactory.getCurrentSession();
System.out.println(sessionNew.get(LanguageEntity.class, 1));
Query anotherQuery = sessionNew.createQuery("from LanguageEntity le where le.languageId < 10");
anotherQuery.setCacheable(true).setCacheRegion("language");
#SuppressWarnings("unchecked")
List<LanguageEntity> languagesFromCache = anotherQuery.list();
System.out.println(languagesFromCache);
sessionNew.getTransaction().commit();
TestTransaction.flagForCommit();
TestTransaction.end();
}
}
UPDATE
One more detail:
All occurences session.getTransaction().commit(); must be removed since they interrupt transaction workflow.
TL;DR
In order to avoid this problem, just remove the first line of the test method and use the already available transaction:
#Test
public void testTransactionCaching() {
// Remove this => TestTransaction.start();
// Same as before
}
Detailed Answer
When you annotate your test class with #Transactional or extending the AbstractTransactionalJUnit4SpringContextTests:
// Other annotations
#Transactional(transactionManager = "hibernateTransactionManager")
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests { ... }
Each test method within that class will be run within a transaction. To be more precise, Spring Test Context (By using TransactionalTestExecutionListener) would open a transaction in the beginning of each test method and roll it back after completion of the test.
So, your testTransactionCaching test method:
#Test
public void testTransactionCaching() { ... }
Would have an open transaction in the beginning and you're trying to open another one manually by:
TestTransaction.start();
Hence the error:
Cannot start a new transaction without ending the existing transaction
first.
In order to avoid this problem, just remove the first line of the test method and use that already available transaction. The other TestTransaction.* method calls are OK but just remove the first one.
Out team is currently programming a JavaEE webapplication for use on a Tomcat appserver.
We want to handle persistence using Hibernate (5.0.1). To access the database entities, we use EntityManagers (not from JPA, they were implemented by us, see below) which provide methods to list, create and delete rows in the associated tables. The model classes use Hibernate Annotations for the mapping.
We also have a static class PersistenceController which initializes Hibernate's SessionFactory and provides a static method to get a newly opened session.
Of course we want to be able to use unit tests to test the functionality of our classes, so the PersistenceController is a little thorn in our eyes.
Someone else recommended me to move everything from the PersistenceController into the EntityManager base class. He wasn't sure if this would have any side consequences though.
So I thought "let's ask the hive mind". What would be the best practice in this case?
(If more code is needed, I'm happy to provide it)
PersistenceController
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class PersistenceController {
private static final SessionFactory sessionFactory;
static {
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
try {
sessionFactory = new MetadataSources(registry).buildMetadata()
.buildSessionFactory();
} catch (Exception e) {
// The registry would be destroyed by the SessionFactory, but we had
// trouble building the SessionFactory
// so destroy it manually.
StandardServiceRegistryBuilder.destroy(registry);
throw e;
}
}
public static Session openSession() {
return sessionFactory.openSession();
}
}
EntityManager
import java.util.List;
public abstract class EntityManager<T extends PersistenceEntity> {
public abstract List<T> listAll();
public abstract void save(T entity);
public abstract void delete(T entity);
}
ProductManager
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.Transaction;
import etepat.model.product.Product;
public class ProductManager extends EntityManager<Product> {
public ProductManager() {
super();
}
#Override
public List<Product> listAll() {
try (Session session = PersistenceController.openSession()) {
Transaction transaction = null;
try {
transaction = session.beginTransaction();
#SuppressWarnings("unchecked")
List<Product> returned = session.createCriteria(Product.class)
.list();
transaction.commit();
return returned;
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
return new ArrayList<Product>();
}
#Override
public void save(Product entity) {
// TODO Auto-generated method stub
}
#Override
public void delete(Product entity) {
// TODO Auto-generated method stub
}
}
The idea of your EntityManager(better call BaseDao or GenericDao) is good but needs some improvements.
First, base CRUD methods don't have to be abstract. They can simply persist/load/delete/list on generic type T. That way you don't have to write these methods on each subclass. See this generic dao approach https://github.com/athanasiosem/Hibernate-Generic-Dao-Java/blob/master/src/main/java/com/demien/hibgeneric/dao/GenericDAOImpl.java
Second is, you're managing transactions manually, is there a good reason to do so?
With container managed transactions(using annotations) you don't need to, and they greatly simplify your code by eliminating the boilerplate try{...}catch{//rollback}.
Basically, with a GenericDao<T> and container managed transactions you don't need this code at all, your classes sublass the GenericDao<ConcreteType> with concrete types and they're ready to do CRUD on database without a single line of code.
I'm trying to create a custom Appender that will persist logs to the database using JPA.
The thing is that I'm using PersistenceContext attribute like this
package com.foobar.logging;
import com.foobar.model.SysLog;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.MDC;
import org.apache.log4j.spi.LoggingEvent;
import javax.ejb.Stateless;
#Stateless
public class LogManager extends AppenderSkeleton {
#PersistenceContext(unitName = "primary")
private EntityManager em;
#Override
protected void append(LoggingEvent le) {
SysLog log = new SysLog();
log.setDescripcion(le.getMessage().toString());
if (MDC.get("IdUsuario") != null) {
log.setIdUsuario(MDC.get("IdUsuario").toString());
}
log.setSysAccionLog(null);
this.em.persist(log);
}
#Override
public void close() {
}
#Override
public boolean requiresLayout() {
return false;
}
}
Now when I'm deploying the WAR to JBoss AS 7.1, it fails, and I get the error:
java.lang.VerifyError: class com.foobar.logging.LogManager$Proxy$_$$_Weld$Proxy$ overrides final method getName.()Ljava/lang/String;
How can I use CDI to inject my EntityManager inside an AppenderSkeleton? Has anyone accomplished JPA persistance in an AppenderSkeleton using CDI?
I also tried not using CDI, but since every other object in my app uses it (JAX-RS classes), it collapses.
EJBs are proxies. AppenderSkeleton has a getName method that is final. I think for your use case, you need to implement Appender directly. This will avoid the bean method getName
However, I have to question the idea of trying to make an appendar an EJB. How are you instantiating it?
While learning spring framework I'm currently on the transaction management topic and while I'm not 100% against using xml I've been trying to do everything using annotations. Along comes this transaction management thing and the instructor just plops it right into the xml file and references a DataSource bean which I created with annotations
... BELOW ...
package com.udemy.learning.main;
import java.sql.SQLException;
import javax.sql.DataSource;
import javax.annotation.*;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component("connect")
public class Connections extends BasicDataSource{
#Value("${jdbc.user}")
private String username;
#Value("${jdbc.password}")
private String password;
#Value("${jdbc.driver}")
private String driverClassName;
#Value("${jdbc.url}")
private String url;
public DataSource connect() throws SQLException{
super.setDriverClassName(this.driverClassName);
super.setUrl(this.url);
super.setUsername(this.username);
super.setPassword(this.password);
return super.createDataSource();
}
#PreDestroy
public void close() {
try {
super.close();
System.out.println("Connection closed");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
So in an attempt to do everything with code and annotations I created a TransactionManager class and just used the #Component annotation above it. I'm sure you can imagine what that looks like so I won't drop that here, plus I'm thinking it's rather elementary looking anyway.
So anyway, long question short...
Is there a way to do this transaction management configuration the way I have attempted? rather than strictly xml? My attempt ended me up with an error like the following...
Bean named 'transactionManager' must be of type [org.springframework.transaction.PlatformTransactionManager], but was actually of type [com.udemy.learning.main.TransactionManager]
You need your bean to be of type PlatformTransactionManager so extend PlatformTransactionManager in your TransactionManager class. The exception is occuring because Spring is expecting any Transaction Manager to be a subclass of the PlatformTransactionManager