资源描述
精品文档就在这里
-------------各类专业好文档,值得你下载,教育,管理,论文,制度,方案手册,应有尽有--------------
--------------------------------------------------------------------------------------------------------------------------------------------
一、数据从持久化的sessio中进入数据库!
save的时候就已经把对象持久化了..在事务commit之前save之后是加到hibernate的session缓存中(也算持久化).. commit之后才把数据写到数据库.
如下语句:
public void save(TLocrecord transientInstance) {
log.debug("saving TLocrecord
instance");
try {
Transaction
tran=getSession().beginTransaction();//获得事物
getSession().save(transientInstance);
mit();//提交事务
log.debug("save
successful");
} catch (RuntimeException re) {
log.error("save failed",
re);
throw re;
}
}
解决办法有两种:
1、
hibernate.cfg.xml
<prop key="hibernate.connection.autocommit">true</prop>
2、使用Spring的事务管理
<bean
id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property
name="sessionFactory" ref="sessionFactory" />
</bean>
二、有级联关系时增删改查
Hibernate 是一个面向对象的持久化框架,其基于关联关系的增删改操作需要从对象的两端进行
Hibernate
基于关联关系的增删改操作主要包括如下几个方面
· 在one方添加数据
·
· 在many方添加数据
·
· 在many方删除数据
·
· 修改关联关系 (的inverse属性 设置更新主控方)
·
· 在one方删除数据(的cascade属性 级联操作)
下面我们逐一对以上进行分析
1.在one方添加数据 — 对于关联关系无明显影响只需要进行 ONE 方操作即可
1
2
3
4
5
6
7
8
9
/**
* ONE 方增加数据不需要对关联属性进行特别设定
* Department department = new Department();
* deparement.setDepartmentName("动力部");
* @param department
*/
public void addDepartment(Department department) {
super.add(department);
}
2.在many方添加数据 — 添加新的雇员需要注意:新雇员需要设设定 雇员姓名 雇员所属部门(关联对象)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Tester {
/**
* 测试 添加新雇员的操作
* 雇员姓名:王华
* 雇员部门:计划部
* @param args
*/
public static void main(String[] args) {
HrDAO dao = new HrDAO();
//新建雇员对象
Employee employee = new Employee();
//雇员姓名装入
employee.setEmployeeName("王华");
//取得 计划部 的部门对象 并装入雇员对象
List<Department> list = dao.getDepartmentList();
for(Department department:list) {
if (department.getDepartmentName().equals("计划部")) {
employee.setDepartment(department);
break;
}
}
dao.addEmployee(employee);
}
}
3.在many方删除数据 — 对于关联关系无明显影响只需要进行 many 方操作即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Tester2 {
/**
* 测试 删除雇员的操作
* 雇员姓名:王华
* @param args
*/
public static void main(String[] args) {
HrDAO dao = new HrDAO();
//新建雇员对象
Employee employee = null;
//根据雇员姓名装入雇员
List<Employee> list = dao.getEmployeeList();
for(Employee e:list) {
if (e.getEmployeeName().equals("王华")) {
employee = e;
break;
}
}
//执行删除
dao.delEmployee(employee.getEmployeeId());
}
}
4.修改关联关系 (的inverse属性 设置更新主控方)
注意:在 one-to-many 双向关联关系中,一般我们将 many 方设置为主控方
one 方为被控方(inverse=’true’)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Tester3 {
/**
* 测试 更新雇员 王五 的部门信息
* 雇员姓名:王五
* 原始部门:财务部
* 新至部门:制造部
* @param args
*/
public static void main(String[] args) {
HrDAO dao = new HrDAO();
//获得雇员对象
Employee employee = null;
//根据雇员姓名装入雇员
List<Employee> list = dao.getEmployeeList();
for(Employee e:list) {
if (e.getEmployeeName().equals("王五")) {
employee = e;
break;
}
}
//获得 财务部 制造部 对象
Department caiwuDepart = null ,zhizaoDepart = null;
//根据部门名称装入部门对象
List<Department> depList = dao.getDepartmentList();
boolean f1 = false , f2 = false;
for(Department d:depList) {
if (d.getDepartmentName().equals("财务部")) {
caiwuDepart = d;
f1 = true;
}
if (d.getDepartmentName().equals("制造部")) {
zhizaoDepart = d;
f2 = true;
}
if (f1 && f2) {
break;
}
}
//1.财务部中删除雇员王华的信息
caiwuDepart.getEmployees().remove(employee);
//2.制造部中添加雇员王华的信息
zhizaoDepart.getEmployees().add(employee);
//2.更新雇员所属部门信息
employee.setDepartment(zhizaoDepart);
//更新数据库
dao.updateEmployee(employee);
}
}
在以上的例子中:
ONE :财务部对象移除雇员 – 制造部对象增加雇员
的操作是没有意义的!
但是这样逻辑上保证了对象,关系数据库模型的统一性。并且在实际应用中如果部门信息并不是直接有数据库中取出,而是由其他瞬时作用域中得到的话,其操作就具有了实际的保证数据一致性的意义!
5.在one方删除数据(的cascade属性 级联操作)
如果在 ONE 方删除数据,MANY
方相关数据不是就会出现无效数据了吗?
解决方案是在 ONE 方 实体配置文件,关联属性中增加 cascade 属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<hibernate-mapping>
<class name=".royakon.entity.Department" table="department" catalog="hrdb">
<id name="departmentId" type="java.lang.Integer">
<column name="department_id" />
<generator class="identity" />
</id>
<property name="departmentName" type="java.lang.String">
<column name="department_name" length="50" not-null="true" />
</property>
<!-- 增加 cascade="all" 属性 所有的 ONE方操作均导致级联更新 -->
<set name="employees" inverse="true" cascade="all" lazy="false">
<key>
<column name="employee_department" not-null="true" />
</key>
<one-to-many class=".royakon.entity.Employee" />
</set>
</class>
</hibernate-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Tester4 {
/**
* 测试 删除 财务部
* @param args
*/
public static void main(String[] args) {
HrDAO dao = new HrDAO();
//获得 财务部 对象
Department caiwuDepart = null;
//根据部门名称装入部门对象
List<Department> depList = dao.getDepartmentList();
for(Department d:depList) {
if (d.getDepartmentName().equals("财务部")) {
caiwuDepart = d;
break;
}
}
//从数据库中删除
dao.delDepartment(caiwuDepart);
}
}
三、spring配置事务初识
事务的定义,事务的作用以及Spring事务原理
(1):事务(Transaction)是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性。
(2):事务通常是以BEGIN TRANSACTION开始,以COMMIT或ROLLBACK结束。
COMMIT表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据库的更新写回到磁盘上的物理数据库中去,事务正常结束。
ROLLBACK表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有以完成的操作全部撤消,滚回到事务开始的状态。
(3):事务运行的三种模式:
A:自动提交事务
每条单独的语句都是一个事务。每个语句后都隐含一个COMMIT。
B:显式事务
以BEGIN TRANSACTION显式开始,以COMMIT或ROLLBACK显式结束。
C:隐性事务
在前一个事务完成时,新事务隐式启动,但每个事务仍以COMMIT或ROLLBACK显式结束。
(4):事务的特性(ACID特性)
A:原子性(Atomicity)
事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。
B:一致性(Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
C:隔离性(Isolation)
一个事务的执行不能被其他事务干扰。
D:持续性/永久性(Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
注:事务是恢复和并发控制的基本单位。
Spring事务原理
统观spring事务,围绕着两个核心PlatformTransactionManager和TransactionStatus
spring提供了几个关于事务处理的类:
TransactionDefinition //事务属性定义
TranscationStatus //代表了当前的事务,可以提交,回滚。
PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。
一般事务定义步骤:
TransactionDefinition td = new TransactionDefinition();TransactionStatus ts = transactionManager.getTransaction(td);try{ //do sth transactionMmit(ts);}catch(Exception e){transactionManager.rollback(ts);}
spring提供的事务管理可以分为两类:编程式的和声明式的。编程式的,比较灵活,但是代码量大,存在重复的代码比较多;声明式的比编程式的更灵活。
编程式主要使用transactionTemplate。省略了部分的提交,回滚,一系列的事务对象定义,需注入事务管理对象.
void add(){ transactionTemplate.execute( new TransactionCallback(){ pulic Object doInTransaction(TransactionStatus ts) { //do sth} }}
声明式:
使用TransactionProxyFactoryBean:<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local="userManagerTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
围绕Poxy的动态代理 能够自动的提交和回滚事务
org.springframework.transaction.interceptor.TransactionProxyFactoryBean
· PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
· PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
· PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
· PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
· PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
· PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
· PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
四、spring配置事务深入
/*2011年8月28日 10:03:30 by Rush */
环境配置
项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可。添加方法:
· 点击项目右键->Build Path->Add librarys:
· 打开Add Libraries对话框,然后选定 MyEclipse Libraries:
· 点击Next,找到Spring 2.0 aop Libraries并勾选上,点击finsh即可。
· 如果在项目里面能看到下面的库文件,说明已经安装成功。
事务配置
· 首先在/WEB-INF/applicationContext.xml添加以下内容:
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
注:这是作为公共使用的事务管理器Bean。这个会是事先配置好的,不需各个模块各自去配。
· 下面就开始配置各个模块所必须的部分,在各自的applicationContext-XXX-beans.xml配置的对于事务管理的详细信息。
首先就是配置事务的传播特性,如下:
<!-- 配置事务传播特性 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="REQUIRED"/>
<tx:method name="apply*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的类 -->
<aop:config>
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.test.testAda.test.model.service.*.*(..))"/>
<aop:advisor pointcut-ref="allTestServiceMethod" advice-ref="TestAdvice" />
</aop:config>
需要注意的地方:
(1) advice(建议)的命名:由于每个模块都会有自己的Advice,所以在命名上需要作出规范,初步的构想就是模块名+Advice(只是一种命名规范)。
(2) tx:attribute标签所配置的是作为事务的方法的命名类型。
如<tx:method name="save*" propagation="REQUIRED"/>
其中*为通配符,即代表以save为开头的所有方法,即表示符合此命名规则的方法作为一个事务。
propagation="REQUIRED"代表支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
(3) aop:pointcut标签配置参与事务的类,由于是在Service中进行数据库业务操作,配的应该是包含那些作为事务的方法的Service类。
首先应该特别注意的是id的命名,同样由于每个模块都有自己事务切面,所以我觉得初步的命名规则因为 all+模块名+ServiceMethod。而且每个模块之间不同之处还在于以下一句:
expression="execution(* com.test.testAda.test.model.service.*.*(..))"
其中第一个*代表返回值,第二*代表service下子包,第三个*代表方法名,“(..)”代表方法参数。
(4) aop:advisor标签就是把上面我们所配置的事务管理两部分属性整合起来作为整个事务管理。
图解:
下面附上配置声明式事务的一些相关的资料,以下资料均来源于互联网:
附一、Spring事务类型详解
附二、对spring事务类型详解的一点补充(关于嵌套事务)
附三、Transaction后缀给声明式事务管理带来的好处
附四、Spring中的四种声明式事务的配置
附一、Spring事务类型详解
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop><prop key="store*">PROPAGATION_REQUIRED</prop>
估计有好多朋友还没有弄清楚里面的值的意思,仔细看完下面应该知道自己什么情况下面应该使用什么样的声明。^_^
Spring中常用事务类型:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
附二、对spring事务类型详解的一点补充(关于嵌套事务)
· PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
· PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
· PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
· PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
· PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
· PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
可能大家对PROPAGATION_NESTED还不怎么了解,觉得有必要再补充一下^_^!
PROPAGATION_NESTED: 嵌套事务类型,是相对上面提到的六种情况(上面的六种应该称为平面事务类型),打个比方我现在有一个事务主要有一下几部分:
1,从A用户帐户里面减去100元钱
2,往B用户帐户里面添加100元钱
这样看和以前不同的事务可能没有什么区别,那我现在有点特殊的要求就是,A用户有3个帐户,B用户有2个帐户,现在我的要求就是只要再A用户的3个帐户里面任意一个减去100元,往B用户的两个帐户中任意一个里面增加100元就可以了!
一旦你有这样的要求那嵌套事务类型就非常适合你!我们可以这样理解,
一:将“从A用户帐户里面减去100元钱” 和 “往B用户帐户里面增加100元钱”我们暂时认为是一级事务操作
二:将从A用户的3个帐户的任意一个帐户里面减钱看做是“从A用户帐户里面减去100元钱”这个一级事务的子事务(二级事务),同样把后面存钱的看成是另一个的二级事务。
问题一:当二级事务被rollback一级事务会不会被rollback?
答案是不会的,二级事务的rollback只针对自己。
问题二:什么时候这个一级事务会commit,什么时候会被rollback呢?
我们主要看二级里面出现的情况,当所有的二级事务被commit了并且一级事务没有失败的操作,那整个事务就算是一个成功的事务,这种情况整个事务会被commit。
当任意一个二级事务没有被commit那整个事务就是失败的,整个事务会被roolback。
还是拿上面的例子来说明吧!如果我在a的三个帐户里面减钱的操作都被二级事务给rollback了,也就是3个帐户里面都没有减钱成功,整个事务就失败了就会被rollback。如果A用户帐户三个帐户里面有一个可以扣钱而且B用户的两个帐户里面也有一个帐户可以增加钱,那整个事务就算成功的,会被 commit。
看了一下觉得上面的例子好像不是很深刻,看这个情况(A用户的3个帐户都是有信用额度的,也就是说可以超支,但是超支有金额限制)。不过原理是一样的,简单点也好说明一点,祝你好运!^_^
附三、Transaction后缀给声明式事务管理带来的好处
良好的面向对象的程序,一般都使用接口和实现分离的模式。我在《事务管理最佳实践全面解析》一文中提出,用*Transaction和*Dao后缀这样的形式,区分方法的不同用途。
这样,可以提醒接口的实现者和方法的使用者注意到它们对于数据库连接和事务的依赖。
实际上,使用*Transaction后缀这样的命名方式,对于声明式事务管理也是很有用处的。如,Spring的事务管理中,我们一般使用方法名的匹配来应用声明式事务。
一、请看下面的Spring配置:
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="*">readOnly</prop>
<prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="modify*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="update*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="delete*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="remove*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="query*">PROPAGATION_REQUIRED, readOnly,-Exception</prop>
<prop key="load*">PROPAGATION_REQUIRED, -Exception</prop>
</props>
</property>
</bean>
这是来自于真实项目中的Spring声明式事务配置。我们对每一个业务层的实现类都应用了这样的事务配置。
我们对所有业务服务Service方法使用了只读事务。对以add,save,modify,update,delete,remove,load开头的方法都使用了事务。
但是,实际上,虽然我们开发的软件一个“信息管理系统”,是围绕数据库开发的。但是,在Service层,我们还是有很多不操作数据库的方法。
如,单纯根据业务逻辑进行计算的,适用缓存进行计算的,执行email发送,文件上传等等任务的方法,在这种配置下都不分青红皂白的应用了事务。
SpringAOP生成的代理对象代理了我们的服务实现类,所有的方法执行前后都被拦截,用来得到和关闭数据库连接,设置、提交和回滚事务。而不管这个方法是否用到了这个数据库。
如果遵照我
展开阅读全文