1

I have an application using Spring 3.2.3 and Hibernate 4.2.1.Final. I made some configurations and the app works pretty well on the test environment, using HSQLDB, etc.

But when the app is deployed it almost works fine. The entity is created but never persisted. I can see the JPA logs:

Hibernate: select nextval ('TASK_SEQ')

But an insert never appears =(

Using the configuration:

 <?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd  
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <context:component-scan base-package="br.com.company" />

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="spring_pu" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <context:annotation-config />
    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

</beans>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<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" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="spring_pu" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>br.com.company.core.entities.Task</class>
        <properties>
                <property name="hibernate.connection.autocommit" value="true" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.format_sql" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
        <property name="hibernate.hbm2ddl.auto" value="update" />

            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
            <property name="hibernate.connection.url" value="jdbc:postgresql://127.0.0.1:5432/companyDB" />
            <property name="hibernate.connection.user" value="postgres" />
            <property name="hibernate.connection.password" value="*****" />
        </properties>
    </persistence-unit>
</persistence> 

The entity:

@Entity
@Table(name = "TASK")
public class Task implements Serializable {

    private static final long serialVersionUID = -6262731134419520342L;

    @Id
    @Column(name = "ID")
    @GeneratedValue(generator = "TASK_SEQ", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "TASK_SEQ", name = "TASK_SEQ")
    private long id;

    @Column(name = "DESCRIPTION")
    private String description;

    @Column(name = "FINISHED")
    private boolean fininshed;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "FINISH_DATE")
    private Date finishDate;

//getters and setter below
}

And finally the service:

@Service
public class TaskService {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional(propagation = Propagation.REQUIRED)
    public void createTask(Task task) {
    <b>//invoked method</b>
        entityManager.persist(task);
    }

As I said, there are no exceptions being thrown but the entity is not persisted like it is at the tests. Thank you!

Edit: I also tried to remove the persistence.xml content into a spring datasource and the problem is still the same:

<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd  
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://127.0.0.1:5432/KCILDS" />
        <property name="username" value="postgres" />
        <property name="password" value="*****" />
    </bean>

    <context:component-scan base-package="br.com.company" />

    <bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="br.com.company.core.entities" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="myEntityManagerFactory" />
    </bean>

    <context:annotation-config />
    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

</beans>

SOLUTION:

I give up the xml configuration. Nothing seems to work with it anymore. Reading more about the tendencies and a lot of configuration I ended up trying successfully a java config and with a few more adjustments will fit perfectly. See below:

    @Configuration
    @EnableTransactionManagement
    @ComponentScan("br.com.company")
    public class PersistenceJPAConfig {

        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
            LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
            factoryBean.setDataSource(this.directDataSource());
            factoryBean.setPackagesToScan(new String[] { "br.com.company" });

            HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
            vendorAdapter.setShowSql(true);

            factoryBean.setJpaVendorAdapter(vendorAdapter);
            factoryBean.setJpaProperties(this.additionlProperties());

            return factoryBean;
        }

        private Properties additionlProperties() {
            Properties properties = new Properties();
            properties.put("database", "POSTGRESQL");
            properties.put("databasePlatform", "org.hibernate.dialect.PostgreSQLDialect");
            properties.put(Environment.SHOW_SQL, true);
            properties.put(Environment.FORMAT_SQL, true);
            return properties;
        }

// now reasearch how to make it an environment configuration
    //  @Bean
    //  public DataSource dataSource() {
    //      JndiDataSourceLookup jndiDataSourceLookup = new JndiDataSourceLookup();
    //      jndiDataSourceLookup.setResourceRef(true);
    //      return jndiDataSourceLookup.getDataSource("jdbc/mybank");
    //  }

        @Bean
        public DataSource directDataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("org.postgresql.Driver");
            dataSource.setUrl("jdbc:postgresql://127.0.0.1:5432/MyBank");
            dataSource.setUsername("postgres");
            dataSource.setPassword("*******");
            return dataSource;
        }

        @Bean //still trying to make a JTA Transaction
        public PlatformTransactionManager transactionManager() {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
            transactionManager.setEntityManagerFactory(this.entityManagerFactoryBean().getObject());
            return transactionManager;
        }

        @Bean
        public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
            return new PersistenceExceptionTranslationPostProcessor();
        }
    }
Marcelo
  • 167
  • 9
  • 1
    I think you have missed call `begin()`(before persist) and `commit()`(after persist) methods in service dude :), should be like this `entityManager.begin(); entityManager.persist(task);entityManager.commit();` –  Jul 14 '13 at 20:37
  • Can I configure the Spring to make those transaction calls (begin and commit) automatically? By the way, the suggestion resulted in an exception:java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:199) – Marcelo Jul 14 '13 at 20:53
  • change this line in xml config `` to `` –  Jul 14 '13 at 20:58
  • and for auto commit mode, add this property `` to the persistence.xml too –  Jul 14 '13 at 21:04
  • The autocommit is already setted. Changing the transaction type resulted on several exceptions. I believe this would required some transactions configurations changes at the context.xml. – Marcelo Jul 14 '13 at 21:20
  • I haven't seen use `RESOURCE_LOCAL` for JEE applications, it's usually `JTA`, and if you are using spring, you have to tells spring to act with entity manager as JTA by adding `` to spring config file –  Jul 14 '13 at 21:42
  • Could you please tell-me more about the JTA transaction type? I guess we can't just change, but this requires a lot of new configurations on my context.xml. Thank you. – Marcelo Jul 14 '13 at 22:55
  • Can you activate the trace level logging for transaction and see if the interceptor is kicking in? Is it working with a plain controller - dao - entitymanager chain called for a button? – Martin Frey Jul 15 '13 at 05:09
  • I'm planning to try it tomorrow and then post the results here. Actually this is the back-end application being invoked through an spring-mvc app. – Marcelo Jul 16 '13 at 03:36

4 Answers4

2

First, you should enable a PersistenceAnnotationBeanPostProcessor bean, like so:

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

As documented in Spring 3.1.x doc, section 14.5.2:

Spring can understand @PersistenceUnit and @PersistenceContext annotations both at field and method level if a PersistenceAnnotationBeanPostProcessor is enabled.

Another thing, make sure your TaskService is scanned. According to the posted Spring configuration file, only the package br.com.company is scanned so TaskService should be under that package.

EDIT

You should use @Transactional for public methods that implement an interface. By default, advice annotation are implemented by Spring via simple Java Proxy, which requires an interface.

As documented in section 8.1.3 AOP Proxies:

Spring AOP defaults to using standard J2SE dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.

Of course, this also implies that TaskService should be referenced by other beans via that interface alone.

yair
  • 8,945
  • 4
  • 31
  • 50
  • Thank you for your response. Just added the PersistenceAnnotationBeanPostProcessor but still not working. Also checked the scan package and all my services, repositories and components are under br.com.company. Am I missing something at the transaction configuration? – Marcelo Jul 15 '13 at 00:15
  • @Marcelo see my new edit, you should use an interface for the annotated method. – yair Jul 15 '13 at 09:12
1

I've faced an issue similar to the one described. I found the problem in incorrect usage of @Transactional notation, in particular I've wrongly used javax.transaction.Transactional instead of org.springframework.transaction.annotation.Transactional

Take a look to the imports of your "@Service".

A detailed description of the difference can be found here at javax.transaction.Transactional vs org.springframework.transaction.annotation.Transactional

Community
  • 1
  • 1
lorenzo
  • 61
  • 1
  • 4
0

As TaskService is defined as a service therefore Spring container won't maintain transactions for CRUD operations. You should delegate CRUD operations to another bean -normally I suffix them with Managers.

So this will be the full hierarchy of transaction workflow:

  1. Create a normal bean. Let's say TaskManager
  2. Inject newly created class into TaskService

@Resource(name = "taskManager")

TaskManager taskManager;

  3 . Inject EntityManager etc in newly created Manager class

  4 . Put all CRUD methods in the manager class

  5 . Call these methods from Service

Call hierarchy: Service -> Manager -> DAO -> Entity Manager

Now you should be able to persist your entity.

Community
  • 1
  • 1
JSS
  • 2,061
  • 1
  • 20
  • 26
  • I had no success here. Just made a TaskRepository and injected the EntityManager. Then used this repository at the Service. Same result =( – Marcelo Jul 14 '13 at 21:15
  • And when you say a DAO layer, what it was supposed to look like? – Marcelo Jul 14 '13 at 22:56
  • 1
    @JSS I believe this isn't true, `@Service` is perfectly fine for declarative transaction. – gerrytan Jul 14 '13 at 22:59
0

I would first doublecheck some obvious stuff: Was the Task type is of br.com.company.Task, not other java api Task ?

If you're still having the same problem, maybe something is wrong with your transaction management. Try flushing the entityManager after you persists. Typically entityManager should automatically flush at the end of transactions:

@Transactional(propagation = Propagation.REQUIRED)
public void createTask(Task task) {
  entityManager.persist(task);
  entityManager.flush(task);
}

If you do have a problem with transaction manager, try instead of specifying datasource inside persistence.xml, create it on spring xml and use dataSource property of JpaTransactionManager

Also set following log4j logger to see transaction start/end and SQL issued by hibernate:

log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
gerrytan
  • 40,313
  • 9
  • 84
  • 99
  • You are right, there is something wrong with transaction management. When I added the flush this exception popped up: javax.persistence.TransactionRequiredException: no transaction is in progress Editing the original post to the actual context.xml. I appreciate your help. – Marcelo Jul 15 '13 at 00:37
  • Updated my answer for you to try move the datasource settings – gerrytan Jul 15 '13 at 01:09
  • I've tried the suggested teste and the problem persisted. I Edited the original thread to add information. It's clear that the transaction is not being activated correctly. Any hint? Thank you! – Marcelo Jul 15 '13 at 02:08
  • Try enable hibernate logging as specified on my answer above, if you can't see hibernate opening/closing transaction, then maybe jdk proxy / aspectj setting problem. To proof this try create a method that uses programmatic transaction – gerrytan Jul 15 '13 at 04:28