事务管理是用来以确保数据库中数据的完整性和一致性。Spring AOP 技术允许开发者管理事务的声明。下面这个例子来说明如何使用 Spring AOP 来管理 Hibernate 事务。在上一篇 Spring 整合 Hibernate 注解方式 文章的基础上。
1. 再创建一张表
CREATE TABLE `comment` (
`cid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`aid` int(10) unsigned NOT NULL,
`comment_content` varchar(254) NOT NULL DEFAULT '',
`postedby` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`cid`)
);
2. Comment的数据访问对象和业务对象
public interface CommentDao {
public void insert(Comment comment);
public void update(Comment comment);
public void delete(Comment comment);
public List<Comment> getComments();
public Comment getCommentById(int id);
}
@Component("commentDao")
public class CommentDaoImpl extends HibernateDaoSupport implements CommentDao {
@Resource
public void setMySessionFactory(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
}
public void insert(Comment comment) {
getHibernateTemplate().save(comment);
}
public void update(Comment comment) {
getHibernateTemplate().update(comment);
}
public void delete(Comment comment) {
getHibernateTemplate().delete(comment);
}
public List<Comment> getComments() {
return (List<Comment>) getHibernateTemplate().find("from Comment");
}
public Comment getCommentById(int id) {
return (Comment) getHibernateTemplate().find("from Comment where cid = ?", id).get(0);
}
}
public interface CommentService {
public void save(Comment comment);
public void update(Comment comment);
public void remove(Comment comment);
public List<Comment> findComments();
public Comment findCommentById(int id);
}
@Service("commentService")
public class CommentServiceImpl implements CommentService {
private CommentDao commentDao;
@Autowired
public void setCommentDao(CommentDao commentDao) {
this.commentDao = commentDao;
}
public void save(Comment comment) {
commentDao.insert(comment);
}
public void update(Comment comment) {
commentDao.update(comment);
}
public void remove(Comment comment) {
commentDao.delete(comment);
}
public List<Comment> findComments() {
return commentDao.getComments();
}
public Comment findCommentById(int id) {
return commentDao.getCommentById(id);
}
}
3. Article的业务对象修改如下
@Transactional(propagation=Propagation.REQUIRED)
@Service("articleService")
public class ArticleServiceImpl implements ArticleService {
private ArticleDao articleDao;
private CommentService commentService;
@Autowired
public void setArticleDao(ArticleDao articleDao) {
this.articleDao = articleDao;
}
@Autowired
public void setCommentService(CommentService commentService) {
this.commentService = commentService;
}
public void save(Article article) {
articleDao.insert(article);
Comment comment = new Comment();
comment.setCommentcontent(article.getTitle() + "写得很好");
comment.setPostedBy("Angelia(默认)");
comment.setArticleId(article.getId());
//int i= 1/0;
commentService.save(comment);
}
public void update(Article article) {
articleDao.update(article);
}
public void remove(Article article) {
articleDao.delete(article);
}
public List<Article> findArticles() {
return articleDao.getArticles();
}
public Article findArticleById(int id) {
return articleDao.getArticleById(id);
}
}
4. 配置文件
<hibernate-configuration>
<session-factory>
<!-- 配置Hibernate的基本属性 -->
<!-- 1.数据源配置到IOC容器中 -->
<!-- 2.关联的.hbm.xml也在IOC容器配置SessionFactory实例 -->
<!-- 3.配置Hibernate的基本属性:方言,SQL显示及格式化,生成数据表的策略以及二级缓存 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<!-- <mapping resource="Article.hbm.xml"/> -->
<mapping class="com.angelia.sh.model.Article"/>
<mapping class="com.angelia.sh.model.Comment"/>
</session-factory>
</hibernate-configuration>
<!-- 配置Spring声明式事务 -->
<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
5. 执行测试类,发现如果执行成功,同时向article和comment各插入一条数据。如果失败,即comment保存之前执行“int i= 1/0;”,则两条数据都插入失败。如果去掉@Transactional(propagation=Propagation.REQUIRED)注解,comment保存之前执行“int i= 1/0;”,只保存了一条数据在库里。
Propagation.REQUIRED是事务传播属性。在事务定义中,必须定义的事务的属性“传播行为”。这意味着,如果一个事务“ArticleServiceImpl.save()方法被调用另外的”commentService.save()'方法,事务应该怎么传播。它是继续在现有的事务中运行,还是为自己开始一个新的事务。
下面是Spring支持的7种事务传播属性:
- PROPAGATION_REQUIRED – 支持当前事务,如果不存在则创建一个新的。
- PROPAGATION_SUPPORTS – 支持当前事务,如果不存在执行非事务。
- PROPAGATION_MANDATORY – 支持当前事务,如果当前不存在事务抛出异常。
- PROPAGATION_REQUIRES_NEW – 创建一个新的事务,如果当前事务暂停。
- PROPAGATION_NOT_SUPPORTED – 不支持当前的事务,而始终执行非事务。
- PROPAGATION_NEVER – 不支持当前的事务,如果当前事务存在则抛出异常。
- PROPAGATION_NESTED – 如果当前存在事务嵌套事务中执行,表现与 PROPAGATION_REQUIRED 一样。
在大多数情况下,可能只需要使用PROPAGATION_REQUIRED。
Spring 事务除了有传播属性,还有隔离级别。下面是Spring支持的五个隔离级别(后面四个与JDBC的隔离级别相对应):
ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
注:1)幻读:事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录;
2)不可重复读取:事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录;
3)脏读:事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。
代码下载链接: https://pan.baidu.com/s/1RhusIDwlUVW-r9NvF-9Mnw 密码: k3yn