41

我是 Spring 世界的新手,我开发了一个使用 Spring 3.2.1 和 Hibernate 4.1.9 来实现 DAO 的简单项目。该项目工作正常,但我对在此 DAO 的 CRUD 方法上使用@Transactional Spring 注释有一些疑问。

这是实现我的项目的 CRUD 操作的类的全部代码:

package org.andrea.myexample.HibernateOnSpring.dao;

import java.util.List;

import org.andrea.myexample.HibernateOnSpring.entity.Person;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.springframework.transaction.annotation.Transactional;

public class PersonDAOImpl implements PersonDAO {

    // Factory per la creazione delle sessioni di Hibernate:
    private static SessionFactory sessionFactory;

    // Metodo Setter per l'iniezione della dipendenza della SessionFactory:
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /** CREATE CRUD Operation:
     * Aggiunge un nuovo record rappresentato nella tabella rappresentato
     * da un oggetto Person
     */
    @Transactional(readOnly = false)
    public Integer addPerson(Person p) {

        System.out.println("Inside addPerson()");

        Session session = sessionFactory.openSession();

        Transaction tx = null;
        Integer personID = null;

        try {
            tx = session.beginTransaction();

            personID = (Integer) session.save(p);
            tx.commit();
        } catch (HibernateException e) {
            if (tx != null)
                tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }

        return personID;

    }

    // READ CRUD Operation (legge un singolo record avente uno specifico id):
    public Person getById(int id) {

        System.out.println("Inside getById()");

        Session session = sessionFactory.openSession();

        Transaction tx = null;          
        Person retrievedPerson = null;  

        try {
            tx = session.beginTransaction();
            retrievedPerson = (Person) session.get(Person.class, id);
            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {                 
            session.close();
        }

        return retrievedPerson;
    }

    // READ CRUD Operation (recupera la lista di tutti i record nella tabella):
    @SuppressWarnings("unchecked")
    public List<Person> getPersonsList() {

        System.out.println("Inside getPersonsList()");

        Session session = sessionFactory.openSession();
        Transaction tx = null;
        List<Person> personList = null;

        try {
            tx = session.beginTransaction();
            Criteria criteria = session.createCriteria(Person.class);
            personList = criteria.list();
            System.out.println("personList: " + personList);
            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {
            session.close();
        }
        return personList;
    }

    // DELETE CRUD Operation (elimina un singolo record avente uno specifico id):
    public void delete(int id) {

        System.out.println("Inside delete()");

        Session session = sessionFactory.openSession();
        Transaction tx = null;

        try {
            tx = session.beginTransaction();
            Person personToDelete = getById(id);
            session.delete(personToDelete);
            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {
            session.close();
        }

    }

    @Transactional
    public void update(Person personToUpdate) {

        System.out.println("Inside update()");

        Session session = sessionFactory.openSession();
        Transaction tx = null;

        try {
            System.out.println("Insite update() method try");
            tx = session.beginTransaction();
            session.update(personToUpdate);

            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {
            session.close();
        }   

    }

}

好的,正如您所看到的,某些方法使用@Transactional 注释进行了注释。

我在这里阅读官方文档http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/transaction.html关于在方法上使用这个注释,它看到:A使用@Transactional 注释的方法必须具有事务语义,但它对事务语义意味着什么?

这是否意味着必须将方法执行视为事务的执行?所以这意味着方法操作必须被视为可能导致成功或失败的单个操作,如果成功,则操作结果必须是永久的,而如果失败则返回到之前的状态交易的开始。

这是在方法上使用@Transactional注解的意思吗?

addPerson()方法的@Transactional 注释中的readOnly = false属性到底是什么意思?这意味着我也可以在数据库中写入一条记录(而不仅仅是读取它)还是什么?这个疑问是相关的,因为我知道,默认情况下,使用 @Transactional 注释定义的事务是读/写的,而不仅仅是读...我还尝试删除(readOnly = false)属性并且仍然可以正常工作(插入数据库表中的新记录)

以下问题是:“为什么使用@Transactional 注释对某些方法进行注释,而对其他一些方法不进行注释?用@Transactional 注释所有CRUD 方法是一种好习惯吗?”

肿瘤坏死因子

安德烈亚

4

2 回答 2

76

首先,您不应该使 DAO 方法具有事务性,而是服务方法。

其次,使用 Transactional 是一种让 Spring 为您启动和提交/回滚事务的方法。所以你不应该自己开始和提交事务。

第三:这仅在您使用知道如何将 Hibernate 会话与事务关联的事务管理器(通常是 a HibernateTransactionManager)时才有效。会话工厂也应该由 Spring 处理,并由 Spring 在您的 DAO 中注入。DAO 的代码应如下所示:

第四:你不应该打开一个新的会话,而是获取当前的一个,通过 Spring 关联到当前事务。

public class PersonDAOImpl implements PersonDAO {

    @Autowired
    private SessionFactory sessionFactory;

    public Integer addPerson(Person p) {
        Session session = sessionFactory.getCurrentSession();
        Integer personID = (Integer) session.save(p);
        return personID;
    }

    public Person getById(int id) {
        Session session = sessionFactory.getCurrentSession();
        Person retrievedPerson = (Person) session.get(Person.class, id);
        return retrievedPerson;
    }

    @SuppressWarnings("unchecked")
    public List<Person> getPersonsList() {
        Session session = sessionFactory.getCurrentSession();
        Criteria criteria = session.createCriteria(Person.class);
        return criteria.list();
    }

    public void delete(int id) {
        Session session = sessionFactory.getCurrentSession();
        Person personToDelete = getById(id);
        session.delete(personToDelete);
    }

    public void update(Person personToUpdate) {
        Session session = sessionFactory.getCurrentSession();
        session.update(personToUpdate);
    }
}

阅读文档以获取更多信息。

于 2013-03-08T18:19:51.053 回答
6

@Transactional在方法上使用。

我们首先在方法级别声明它打开事务,执行操作并关闭事务。

如果操作失败则回滚,如果操作成功则自动提交

这是关于@Transactional最后的注释和简短的。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    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.xsd
        ">

    <!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
    <context:component-scan base-package="hu.daniel.hari.learn.spring" />
    <!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
    <context:annotation-config />


    <!-- creating the internal datasource object -->

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem://productDb" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
            p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
            p:dataSource-ref="dataSource"
            >
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="true" />
                <property name="showSql" value="true" />

            </bean>
        </property>
    </bean>

    <!-- Transactions -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>
package hu.daniel.hari.learn.spring.orm.main;

import hu.daniel.hari.learn.spring.orm.model.Product;
import hu.daniel.hari.learn.spring.orm.service.ProductService;

import java.util.Arrays;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;

public class SpringOrmMain {

    public static void main(String[] args) {

        //Create Spring application context
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");

        //Get service from context. (service's dependency (ProductDAO) is autowired in ProductService)
        ProductService productService = ctx.getBean(ProductService.class);

        //Do some data operation

        productService.add(new Product(1, "Bulb"));
        productService.add(new Product(2, "Dijone mustard"));

        System.out.println("listAll: " + productService.listAll());

        //Test transaction rollback (duplicated key)

        try {
            productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
        } catch (DataAccessException dataAccessException) {
        }

        //Test element list after rollback
        System.out.println("listAll: " + productService.listAll());

        ctx.close();
    }
}
于 2018-05-28T09:19:46.320 回答