user1882491
user1882491

Reputation: 111

How to save entity using @Transactional from Spring

Have a simple Spring-Hibernate application with the following config:

App context:

<context:component-scan base-package="org.hibtests" />

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />

    <property name="annotatedClasses">
        <list>
            <value>org.hibtests.domain.Person</value>
        </list>
    </property>

    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.current_session_context_class">thread</prop>
        </props>
    </property>
</bean>

<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/hibernatedb" />
    <property name="username" value="dbuser" />
    <property name="password" value="blahblah" />
</bean>


<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"></property>
</bean>

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

The main application looks like

ApplicationContext context = new ClassPathXmlApplicationContext("simpleappcontext.xml");
PersonService personService = (PersonService) context.getBean("personService");

Person person = new Person();
person.setName("Alba");
person.setEmail("[email protected]");
personService.addPerson(person);

with the PersonService being annotated @Transactional:

package org.hibtests.service;

@Transactional
@Component
public class PersonService {

    @Autowired
    private PersonDao personDao;

    public PersonDao getPersonDao() {
        return personDao;
    }


    @Transactional(readOnly=false)
    public void addPerson(Person person) {
        getPersonDao().insert(person);
    }
}

PersonDao.java:

package org.hibtests.dao;

@Repository
public class PersonDao {

    @Autowired
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void insert(Person person) {
        Session session = getSessionFactory().getCurrentSession();
        session.persist(person);
    }

produces the error

INFO: Using DataSource [org.springframework.jdbc.datasource.DriverManagerDataSource@1fe4169] of Hibernate SessionFactory for HibernateTransactionManager
Exception in thread "main" org.hibernate.HibernateException: persist is not valid without active transaction
    at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:348)
    at $Proxy16.persist(Unknown Source)
    at org.hibtests.dao.PersonDao.insert(PersonDao.java:26)
    at org.hibtests.dao.PersonService.addPerson(PersonService.java:27)
    at org.hibtests.dao.PersonService$$FastClassByCGLIB$$403bca40.invoke(<generated>)

while I could use session.beginTransaction() and session.getTransaction().commit() before and after the persist method, is it possible to just use Spring's @Transactional annotations to manage the transaction? If so how?

Upvotes: 0

Views: 1672

Answers (3)

M. Deinum
M. Deinum

Reputation: 124441

<property name="hibernateProperties">
<props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.current_session_context_class">thread</prop>
</props>
</property>

It is your own configuration that is breaking proper transaction support/integration. When using Spring for declarative transaction management never mess around with the hibernate.current_session_context_class property. Unless you are using JTA then you need to integrate it with JTA.

Spring by default registers its own CurrentSessionContext implementation for proper transaction integration with hibernate. It will register the SpringSessionContext. However as soon as you start configuring the hibernate.current_session_context_class this default will be overridden and as such transactions break.

Remove the line from your configuration.

<property name="hibernateProperties">
    <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
</props>
</property>

This will properly let spring register its own contextual session management.

Upvotes: 2

lorinpa
lorinpa

Reputation: 556

You can annotate your insert method so Spring injects a transaction:

@Transactional
public void insert(Person person) {

And you PersonDao class should be implementing an interface. The "proxy" exception means Spring can't inject the transaction without an (annotated) interface. You need to add an interface an have PersonDao implement the interface.

Hope that helps.

Upvotes: 0

Ashish Jagtap
Ashish Jagtap

Reputation: 2819

Update your service class as follows

@Service
@Transactional(readOnly = true,propagation=Propagation.SUPPORTS)
public class PersonService {
}

OR you can do this way also

package org.hibtests.service;

@Service
public class PersonService {

    @Autowired
    private PersonDao personDao;

    @Transactional(readOnly = true,propagation=Propagation.SUPPORTS)
    public PersonDao getPersonDao() {
        return personDao;
    }


    @Transactional(readOnly = true,propagation=Propagation.SUPPORTS)
    public void addPerson(Person person) {
        getPersonDao().insert(person);
    }
}

hope this will help you

Upvotes: 0

Related Questions