I have the following test case:
#ContextConfiguration("/spring/test-context.xml")
#TransactionConfiguration(transactionManager="txManager")
#Transactional()
public class MyEntityDaoTestCase extends AbstractJUnit4SpringContextTests {
#Autowired
private MyEntityDao dao;
#Test
public void testSave_success() {
MyEntity e = new MyEntity();
dao.save(e);
MyEntity result = dao.findById(e.getId());
assertNotNull(result);
}
}
My DAO definition has as follows:
public abstract class MyEntityDAO {
#PersistenceContext
private EntityManager mEntityManager;
public void save(MyEntity entity) {
mEntityManager.persist(entity);
}
public MyEntity findById(Long id) {
return mEntityManager.find(mEntityClass, id);
}
}
My Spring config is the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!--
Bean post-processor for JPA annotations
-->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<!--
JPA entity manager factory
-->
<bean id="jpaEntityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="unit-test-pu"/>
</bean>
<!--
Transaction manager
-->
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="jpaEntityManagerFactory"/>
</bean>
<!--
Enable the configuration of transactional behavior based on annotations
-->
<tx:annotation-driven transaction-manager="txManager"/>
<!--
DAO instance beans
-->
<bean id="mockEntityDao" class="mypackage.MyEntityDao"></bean>
</beans>
I get no errors while executing my test but it won't pass. It looks like the findById() method will not find the entity in the database. Can anyone advise on how to correctly test this case?
EDIT:
My JPA provider is hibernate. I am using an in-memory HSQLDB for my unit tests and have the following configuration:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="unit-test-pu" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:."/>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
You could try using #TransactionalConfiguration annotation and the Spring JUnit runner.
Something like changing your class to this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/spring/test-context.xml")
#TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
#Transactional
public class MyEntityDaoTestCase {
This also means you don't need to extend the abstract case (because you are using the Spring runner) - unless you particularly like that approach.
Here more details
If you follow TDD strictly you should not use an in memory database but instead have everything mocked. The problem is that the persist method returns void. So you can not test the correct response (an entity with an id generated by a database) One way to work around this is to us the Mockito doAnswer method, here an example:
#RunWith(MockitoJUnitRunner.class)
public class CookieRepositoryTest {
#Mock
EntityManager em;
#Mock
TimeService timeService;
#InjectMocks
CookieRepository underTest = new CookieRepository();
#Test
public void testCreateEntity() throws Exception {
Cookie newCookie = new Cookie();
when(timeService.getTime()).thenReturn(new DateTime(DateTimeZone.UTC));
doAnswer(new Answer<Brand>() {
#Override
public Brand answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] args = invocationOnMock.getArguments();
Cookie cookie = (Cookie) args[0];
cookie.setId(1);
return null;
}
}).when(em).persist(any(Brand.class));
Cookie persistedCookie = underTest.createEntity(newCookie);
assertNotNull(persistedCookie.getId());
}
}
A complete explanation can be found on my blog post.
I think you should be extending AbstractTransactionalJUnit4SpringContextTests instead of AbstractJUnit4SpringContextTests for the #Transactional-annotations to have any effect.
If you want to test the persistence layer, you could also take a look at DBUnit capabilities.
You can find a nice article from Petri Kainulainen here about testing the persistence layer (in this case with JPA) in a Spring based scenario:
http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-integration-testing/
With this, you test if the DAO classes behave as expected, writing and reading into/from the DB, and on the service layer you can avoid testing this aspect, focusing more on the "mock" approach for the business logic.
Hope it helps
Fran
Related
CONTEXT
I've been trying to create a generic persistence layer for my application using Camel (v2.23.0) and Hibernate(v5.3.7). I'm also using HikariCP as the connection pool. To implement this generic persistence layer, I'm using the Camel JPA component to construct the core persistence routes as follows:
from("direct:getEntityById")
.toD("jpa:${header.entityClassName}?persistenceUnit=" + projectStage.toString() +
"&namedQuery=${header.entityClassSimpleName}.${header.queryName}");
from("direct:upsertEntity")
.toD("jpa:${header.entityClassName}?usePersist=false&persistenceUnit=" + projectStage.toString());
In the DAO, I'm using a dynamic JPA endpoint, so that I can reuse the route for any JPA entity.
I'm calling these routes through a plain Java DAO class, as follows:
...
public <T> Optional<T> getEntityById(Object id, String queryName, Class<T> clazz) throws GenericDAOException {
try {
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("id", id);
List<T> entities = (List<T>) fluentProducerTemplate
.withHeader("id", id)
.withHeader("entityClassName", clazz.getName())
.withHeader("entityClassSimpleName", clazz.getSimpleName())
.withHeader("queryName", queryName)
.withHeader("CamelJpaParameters", queryParams)
.to("direct:getEntityById")
.request(List.class);
...
}
...
}
#Override
public <T> void upsertEntity(T entityObject) throws GenericDAOException {
try {
fluentProducerTemplate
.withBody(entityObject)
.withHeader("entityClassName", entityObject.getClass().getName())
.to("direct:upsertEntity")
.request();
}
...
}
This is the configuration for my persistence unit:
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Production" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>...</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://..."/>
<property name="javax.persistence.jdbc.user" value="..."/>
<property name="javax.persistence.jdbc.password" value="..."/>
<property name="hibernate.dialect" value="<custom dialect>"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.hikari.minimumIdle" value="25"/>
<property name="hibernate.hikari.maximumPoolSize" value="70"/>
<property name="hibernate.hikari.idleTimeout" value="300000"/>
<property name="hibernate.hikari.maxLifetime" value="600000"/>
<property name="hibernate.connection.provider_class"
value="org.hibernate.hikaricp.internal.HikariCPConnectionProvider"/>
</properties>
</persistence-unit>
</persistence>
Also, my JPA component is being created using a specific EntityManagerFactory (so I'm assuming all JPA endpoints will be sharing the same factory). (Using CDI for dependency injections)
#Produces
#ApplicationScoped
#Named("jpa")
public JpaComponent getComponent(PlatformTransactionManager transactionManager, EntityManager entityManager) {
JpaComponent component = new JpaComponent();
component.setTransactionManager(transactionManager);
component.setEntityManagerFactory(entityManager.getEntityManagerFactory());
component.setCamelContext(camelContext);
return component;
}
QUESTIONS:
As per the following doc: https://camel.apache.org/components/latest/eips/toD-eip.html, we should avoid creating endless dynamic endpoints, due to the excessive resource consumption. Since I'm working with the JPA component, I am especially concerned about the number of DB connections created, which could lead to application failures if not within a certain threshold.
To get around these potential issues, I'm using a connection pool. Since all JPA endpoints would use the same EntityManagerFactory, I presume that the connection pool would also be shared between these endpoints.
So I have the following questions:
When exactly would the JPA route release the DB connection? (as in, are they released immediately after the JPA route has completed processing a message, or are the connections tied to the lifecycle of the JPA endpoints?)
Would a new dynamic JPA endpoint be created for each unique URI?
What are the other potential issues that I would need to be wary of, when using dynamic JPA endpoints? (I suppose memory consumption would be one).
I'm trying to build a simple application, which connects with database and saves some data in it, let's say once per hour. I found some tutorials on pages like baeldung, but their solutions doesn't work for me.
Here is my configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<context:component-scan base-package="io.github.steve"/>
<!-- Step 4: Add support for conversion, formatting and validation support -->
<mvc:annotation-driven/>
<!-- Step 5: Define Spring MVC view resolver -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="messages"/>
</bean>
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/articles?useSSL=false"/>
<property name="user" value="root"/>
<property name="password" value=""/>
<property name="minPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="maxIdleTime" value="30000"/>
</bean>
<bean id="sessionFactoryXXX" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="packagesToScan" value="io.github.steve.webscraping.domain"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="myTransactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryXXX"/>
</bean>
<tx:annotation-driven transaction-manager="myTransactionManager"/>
</beans>
ArticlesDaoImpl:
#Repository
public class ArticlesDaoImpl implements ArticlesDao {
#Autowired
SessionFactory sessionFactory;
#Override
public List<Article> getAllArticles() {
Session session = sessionFactory.getCurrentSession();
Query<Article> query = session.createQuery("from Article order by uploadDate", Article.class);
return query.getResultList();
}
#Override
public void addArticle(Article article) {
Session session = sessionFactory.getCurrentSession();
session.persist(article);
}
}
ArticlesServiceImpl:
#Service
public class ArticlesServiceImpl implements ArticlesService {
#Autowired
private ArticlesDao articlesDao;
#Override
#Transactional
public List<Article> getAllArticles() {
return articlesDao.getAllArticles();
}
#Override
#Transactional
public void addArticle(Article article) {
articlesDao.addArticle(article);
}
#Override
#Transactional
public void addArticles(List<Article> articles) {
articles.stream().forEach(articlesDao::addArticle);
}
}
And now:
First of all, I don't know where should I put my xml config file.
I have no idea how to build my Main.class. I want to Autowire ArticlesService to Main class and run a method from it. When i use just SpringApplication.run(Main.class, args), it loads and finishing with exit code 1.
Main app:
#SpringBootApplication
#EnableScheduling
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
ScheduledTasks:
#Component
public class ScheduledTasks {
#Autowired
ArticlesService articlesService;
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
#Scheduled(cron = "* * * * *")
public void reportCurrentTime() {
articlesService.addArticles(new MkyongWebScraper().getArticlesList());
}
}
I'm really new in Spring and I know this question is pretty wide, but I can't find any suitable sources for my tasks. What am I doing wrong?
I am afraid the code you are writing, especially the XML config does not take advantage of the easier and more convenient offered with Spring Boot, and Spring Boot 2.
To generate the skeleton of your project, go to Spring Boot Initializer. You can choose the dependencies you want (in your case, it should "JPA" which is based on Spring Data JPA) and then you can download a zip with your project.
You might want to have a look at Spring Boot documentation. There are also some pretty interesting videos to "get started with Spring Boot" on youtube..
Once you have your project, you can add Spring Data JPA code...
Spring Boot documentation has a section about Spring Data JPA, but you can also find more info in the documentation of Spring Data JPA project.
Best of luck
I already solved my problem. I just didn't understood Spring enough. For people like me I recommend Spring in Action by Craig Walls.
I'm getting the above exception with Spring3 and Hibernte4
The following is my bean xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:annotation-config/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/GHS"/>
<property name="username" value="root"/>
<property name="password" value="newpwd"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.example.ghs.model.timetable</value>
</list>
</property>
</bean>
<bean id="baseDAO"
class="com.example.ghs.dao.BaseDAOImpl"/>
</beans>
My BaseDAO class looks like this
public class BaseDAOImpl implements BaseDAO{
private SessionFactory sessionFactory;
#Autowired
public BaseDAOImpl(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Override
public Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
}
The following code throws the exception in the title
public class Main {
public static void main(String[] args){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("dao-beans.xml");
BaseDAO bd = (BaseDAO) context.getBean("baseDAO");
bd.getCurrentSession();
}
}
Does anyone have an idea about how to solve this problem?
getCurrentSession() only makes sense inside a scope of transaction.
You need to declare an appropriate transaction manager, demarcate boundaries of transaction and perform data access inside it. For example, as follows:
<bean id = "transactionManager" class = "org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name = "sessionFactory" ref = "sessionFactory" />
</bean>
.
PlatformTransactionManager ptm = context.getBean(PlatformTransactionManager.class);
TransactionTemplate tx = new TransactionTemplate(ptm);
tx.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
// Perform data access here
}
});
See also:
10. Transaction Management
13.3 Hibernate
I came across same problem and got solved as below
Added #Transactional on daoImpl class
Added trnsaction manager in configuration file:
<tx:annotation-driven/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
I'll just add something that took me some time to debug : don't forget that a #Transactional annotation will only work on "public" methods.
I put some #Transactional on "protected" ones and got this error.
Hope it helps :)
http://docs.spring.io/spring/docs/3.1.0.M2/spring-framework-reference/html/transaction.html
Method visibility and #Transactional
When using proxies, you should apply the #Transactional annotation
only to methods with public visibility. If you do annotate protected,
private or package-visible methods with the #Transactional annotation,
no error is raised, but the annotated method does not exhibit the
configured transactional settings. Consider the use of AspectJ (see
below) if you need to annotate non-public methods.
Which package u have put the BaseDAOImpl class in.. I think It requires a package name similar to the one u have used in the application context xml and it requires a relevant annotation too.
I am using native method of entity manager and I want to rollback when some error occurs.For this I tried #Transactional annotation but this does not rollback.Below is my sample code
controller
#Autowired
ServiceImpl ServiceImpl;
#RequestMapping("/saveinfo")
#ResponseBody
#Transactional
public String saveinfo(Long id)
{
ServiceImpl.saveInfo(id);
}
Service class
#Autowired
DAOImpl daoImpl;
#Transactional
public String saveinfo(Long id)
{
daoImpl.saveInfo1(id);
daoImpl.saveInfo12(id);
daoImpl.saveInfo12(id);
}
DAO class
#Override
public BigInteger saveInfo11() {
Query query = entityManagerUtil.entityManager().createNativeQuery("insert query");
return (BigInteger)query.getSingleResult();
}
#Override
public BigInteger saveInfo12() {
Query query = entityManagerUtil.entityManager().createNativeQuery("insert query");
return (BigInteger)query.getSingleResult();
}
#Override
public BigInteger saveInfo13() {
Query query = entityManagerUtil.entityManager().createNativeQuery("insert query");
return (BigInteger)query.getSingleResult();
}
Now in the above codes,
If I have some runtime error in saveInfo3() then I want to rollback methods of
saveInfo1() and saveInfo2()
This is the way I did but it did not rollback,so Please tell me how to do
EDIT
I tried using
#Transactional(rollbackFor=MyException.class,propagation = Propagation.REQUIRED) and #Transactional(propagation = Propagation.REQUIRED) and
#Transactional(rollbackFor=MyException.class))
In all the 3 cases ,it did not rollback
Update
applicationcontext.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!--<context:annotation-config />-->
<context:spring-configured/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="persistenceUnit"/>
</bean>
<bean id="messageDigestPasswordEncoder" class="org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder">
<constructor-arg value="SHA-256" />
</bean>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<context:property-placeholder location="classpath*:META-INF/database.properties"/>
<context:property-placeholder location="classpath*:META-INF/project.properties"/>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="propertiesUtil" class="com.work.project.utils.PropertiesUtil">
<property name="locations" >
<list>
<value>classpath*:META-INF/*.properties</value>
</list>
</property>
</bean>
<context:component-scan base-package="com.work.project">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<task:executor id="myexecutor" pool-size="5" />
<task:annotation-driven executor="myexecutor"/>
</beans>
modified controller method
#Autowired
ServiceImpl ServiceImpl;
#RequestMapping("/saveinfo")
#ResponseBody
//Now I dont use transactional annotation in controller class
public String saveinfo(Long id)
{
ServiceImpl.saveInfo(id);
}
If any more information is required please ask
The problem seems to be that the EntityManager is not injected by Spring. The EntityManager instance is created by your utility class entityManagerUtil.entityManager(). This means, that everytime when you use a new EntityManager, they are not part of your method transaction.
In order to solve this problem: let Spring inject correctly the EntityManager (try for example injecting it directly in your original bean with #PersistenceContext and do those three methods directly in the same single method).
UPDATE:
The problem was that the code that threw the exception was in a try/catch block, this way, the transaction was not rolled-back by Spring. Transaction is rolled-back only when the transactional method exits with an RuntimeException (by default).
You need to change your method to have rollbackFor property for your #Transactional annotation, so that
#Transactional(rollbackFor=MyException.class)
public String saveinfo(Long id)
Keep in mind, that MyException should be unchecked exception, as checked exceptions thrown from #Transactional method will not result transaction to be rolled back.
Update:
To make sure everything is correct
Check that Spring indeed creates proxy and your method is being executed in transaction (e.g. use TransactionSynchronizationManager.isActualTransactionActive())
Check that MyException.class is indeed unchecked exception (subclass of java.lang.RuntimeException or java.lang.Error)
Check that your exception is indeed thrown (e.g. not catched somehow)
And why your controller saveInfo() is marked as transactional? This it not recommended, only service layer should be marked as transactional. See this post for example.
Maybe the entityManagerUtil (which you use within your DAO implementation) uses or creates an entity manager, that is not running in the surrounding transaction context. Please have a look at following tutorial: Here the JPA EntityManager is injected with #PersistenceContext
Can you make sure that your persistenceUnit has configured the following, transaction type(optional) and hibernate.connection.autocommit to false, if not please do so.
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.connection.autocommit" value="false" />
</properties>
</persistence-unit>
I have excluded other properties for brevity
In my project I use Seam 3 and I am having issues injecting the EntityManager with the #Inject annotation. I am pretty sure there is some kind of configuration to make sure the EnityManager knows which PersistenceUnit to use. For example with EJB you can type:
#PersistenceContext(unitName="MY_PERSISTENCE_UNIT_NAME")
private EntityManager eManager;
which persistence unit is configured in the persistence.xml file. Here is my pseudo configuration:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="MY_PERSISTENCE_UNIT_NAME" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/TimeReportDS</jta-data-source>
<mapping-file>META-INF/orm.xml</mapping-file>
<class>....</class>
<class>....</class>
<class>....</class>
<properties>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/modelEntityManagerFactory" />
<!-- PostgreSQL Configuration File -->
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.connection.password" value="password" />
<property name="hibernate.connection.url" value="jdbc:postgresql://192.168.2.125:5432/t_report" />
<property name="hibernate.connection.username" value="username" />
<!-- Specifying DB Driver, providing hibernate cfg lookup
and providing transaction manager configuration -->
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
<property name="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.JBossTransactionManagerLookup" />
<property name="hibernate.archive.autodetection" value="class" />
<!-- Useful configuration during development - developer can see structured SQL queries -->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="false" />
</properties>
</persistence-unit>
</persistence>
I have read some articles about Seam 2 but there the configuration is made in components.xml file by adding the:
<persistence:managed-persistence-context
name="entityManager" auto-create="true" persistence-unit-jndi-name="java:/modelEntityManagerFactory" />
inside the <components> tag. The next step in Seam 2 is to add the:
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/modelEntityManagerFactory" />
in the persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence ...>
<persistence-unit name="MY_PERSISTENCE_UNIT_NAME" ...>
...
<properties>
...
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/modelEntityManagerFactory" />
</properties>
</persistence-unit>
</persistence>
but it seam that in Seam 3 there is no file components.xml. Also there is no attribute unitName in #Inject annotation to specify the persistence unit.
So please help me to configure my project so I can use the #Inject with EntityManager as shown in many examples on the net.
I use Postgres database and JBoss AS 7.
EDIT: Adding an example. I don't use the EntityManager in an Entity class.
#Named("validateReportAction")
#SessionScoped
public class ValidateReportAction extends ReportAction implements Serializable {
private static final long serialVersionUID = -2456544897212149335L;
#Inject
private EntityManager em;
...
}
Here in this #Inject I get warning from Eclipse "No bean is eligible for injection to the injection point [JSR-299 ยง5.2.1]"
If I use the #Inject on some beans that are marked as Entity the #Inject works fine.
You can use #PersistenceContext on a CDI bean. It doesn't have to be an EJB.
If for some reason you want to use #Inject, you have to do more work. #Inject doesn't know about the EntityManager; it can only inject other managed beans. Happily, there is a simple workaround - use a producer method that acts as a simple trampoline.
#ApplicationScoped
public class EntityManagerProducer {
#PersistenceContext
private EntityManager entityManager;
#Produces
#RequestScoped
public EntityManager getEntityManager {
return entityManager;
}
public void closeEntityManager(#Disposes EntityManager em) {
if (em != null && em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
if (em != null && em.isOpen()) {
em.close();
}
}
}
You can now use #Inject to inject the EntityManager. The injected EntityManager will be RequestScoped, while the EntityManagerProducer is ApplicationScoped. Furthermore, the entityManager must be closed.