接着上一篇博客来写。。。
下面来看一下如何将Spring管理事物应用到实际的项目中,为了简化实现,这里只定义最简单的模型对象和不完整的DAO和Service层接口
1、定义项目中的模型对象,这里使用用户模型和用户的地址模型:模型对象一般放在model包里
public class UserModel {
private int id;
private String user_name;
private String password;
private AddressModel address;
省略getter和setter方法
public class AddressModel {
private int id;
private String province;
private String city;
private String street;
private int userid;
省略getter和setter方法
2.1、定义DAO层接口 DAO层实现具体的数据访问逻辑,屏蔽对数据访问技术造成的差异
package com.spring.DAO;
public interface IUserDAO {
public KeyHolder save(UserModel user);
public int countAll();
}
package com.spring.DAO;
public interface IAdressDAO {
public KeyHolder save(AddressModel address);
public int countAll();
}
2.2、DAO层实现
package com.spring.DAOImpl;
public class UserJdbcDAOImpl extends NamedParameterJdbcDaoSupport implements IUserDAO{
private final String INSERT_SQL="insert into td(user_name,password)values(:user_name,:password)";
private final String COUNT_ALL_SQL="select count(*)from td";
@Override
public KeyHolder save(UserModel user) {
KeyHolder keyHolder=new GeneratedKeyHolder();
/**
* SqlParameterSource:可以使用SqlParameterSource实现作为来实现为命名参数设值,
* 默认有MapSqlParameterSource和BeanPropertySqlParameterSource实现;
* MapSqlParameterSource实现非常简单,只是封装了java.util.Map;
* 而BeanPropertySqlParameterSource封装了一个JavaBean对象,通过JavaBean对象属性来决定命名参数的值。
*/
SqlParameterSource paramSource=new BeanPropertySqlParameterSource(user);
/**
* 设置如何获取自增主键值
*/
getNamedParameterJdbcTemplate().update(INSERT_SQL, paramSource,keyHolder,new String[]{"id"});
return keyHolder;
}
@Override
public int countAll() {
return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);
}
}
package com.spring.DAOImpl;
public class AddressJdbcDAOImpl extends NamedParameterJdbcDaoSupport implements IAdressDAO{
private final String INSERT_SQL="insert into address(province,city,street,userid) values(:province,:city,:street,:userid)";
private final String COUNT_ALL_SQL="select count(*)from address";
@Override
public int countAll() {
return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);
}
/**
* 这里表示了如何获取主键自增值
*/
@Override
public KeyHolder save(AddressModel address) {
KeyHolder keyHolder=new GeneratedKeyHolder();
SqlParameterSource paramSource=new BeanPropertySqlParameterSource(address);
/**
* 注意下面的参数列表
*/
getNamedParameterJdbcTemplate().update(INSERT_SQL, paramSource,keyHolder,new String[]{"id"});
return keyHolder;
}
}
3.1、定义Service层接口,一般使用IxxxServeice命名,Service类对象的是业务类
package com.spring.Service;
public interface IUserService {
public void save(UserModel user);
public int countAll();
}
package com.spring.Service;
public interface IAdressService {
public void save(AddressModel address);
public int countAll();
}
3.2、定义Service层实现,一般使用“×××ServiceImpl”或“×××Service”命名:
package com.spring.serviceImpl;
public class UserServiceImpl implements IUserService{
private IUserDAO userDAO;
private IAdressService adressService;
public void setAdressService(IAdressService adressService) {
this.adressService = adressService;
}
private PlatformTransactionManager txManager;
public IUserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
}
public PlatformTransactionManager getTxManager() {
return txManager;
}
public void setTxManager(PlatformTransactionManager txManager) {
this.txManager = txManager;
}
@Override
public void save(final UserModel user) {
TransactionTemplate transactionTemplate=TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
/**
* 如何获取自增主键值
*/
KeyHolder key=userDAO.save(user);
System.out.println(key.getKey().intValue());
user.getAddress().setUserid( key.getKey().intValue());
System.out.println(user.getAddress().getUserid());
System.out.println(user.getAddress().getProvince());
adressService.save(user.getAddress());
}
});
}
@Override
public int countAll() {
return userDAO.countAll();
}
}
package com.spring.serviceImpl;
public class AddressServeiceImpl implements IAdressService{
private IAdressDAO adressDAO;
private PlatformTransactionManager txManager;
public void setAdressDAO(IAdressDAO adressDAO) {
this.adressDAO = adressDAO;
}
public void setTxManager(PlatformTransactionManager txManager) {
this.txManager = txManager;
}
@Override
public void save(final AddressModel address) {
TransactionTemplate transactionTemplate=TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
KeyHolder keyHolder=adressDAO.save(address);
System.out.println(keyHolder.getKey().intValue());
}
});
}
@Override
public int countAll() {
return adressDAO.countAll();
}
}
4、定义TransactionTemplateUtils,用于简化获取TransactionTemplate模板类,工具类一般放在util包中:
package com.spring.util;
public class TransactionTemplateUtils {
public static TransactionTemplate getTransactionTemplate(PlatformTransactionManager txManager,
int propagationBehavior,
int isolationLevel){
TransactionTemplate transactionTemplate=new TransactionTemplate(txManager);
transactionTemplate.setPropagationBehavior(propagationBehavior);
transactionTemplate.setIsolationLevel(isolationLevel);
return transactionTemplate;
}
/**
*
* @param txManager
* @return getDefaultTransactionTemplate用于获取传播行为为PROPAGATION_REQUIRED,隔离级别为ISOLATION_READ_COMMITTED的模板类。
*/
public static TransactionTemplate getDefaultTransactionTemplate(PlatformTransactionManager txManager){
return getTransactionTemplate(txManager,TransactionDefinition.PROPAGATION_REQUIRED,
TransactionDefinition.ISOLATION_READ_COMMITTED);
}
}
6、配置文件
<!-- datasource for oracle -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"></property>
<property name="username" value="scott"></property>
<property name="password" value="123"></property>
</bean>
<!-- DAO层配置定义 -->
<bean id="userDAO" class="com.spring.DAOImpl.UserJdbcDAOImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service层配置定义 -->
<bean id="userService" class="com.spring.serviceImpl.UserServiceImpl">
<property name="userDAO" ref="userDAO"></property>
<property name="txManager" ref="txManager"></property>
<property name="adressService" ref="addressService"></property>
</bean>
<bean id="addressDAO" class="com.spring.DAOImpl.AddressJdbcDAOImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="addressService" class="com.spring.serviceImpl.AddressServeiceImpl">
<property name="adressDAO" ref="addressDAO"></property>
<property name="txManager" ref="txManager"></property>
</bean>
8、准备测试需要的表创建语句,这里我只创建了address表,因为之前已经创建过用户表在创建表时我实现了主键自增,所以同时要创建序列和引擎
package com.spring.test;
public class springTransactionTest {
/**
* 创建address表
*/
public static final String CREATE_ADDRESS_TABLE_SQL="create table address(id int,province varchar2(40),city varchar2(100),"
+ "street varchar2(100) ,userid int,primary key (id))";
/**
* address:创建序列
*/
public static final String CREAT_SQU="create sequence se start with 1"
+ "increment by 1 minvalue 1 maxvalue 999999 nocycle nocache";
/**
* address:实现主键自增
*/
public static final String KEY_INCRE_SQL="insert into address(id) values (se.NEXTVAL)";
/**
* address:创建引擎
*/
public static final String TRIGGER_SQL="create trigger trie before insert on address for each row begin select se.NEXTVAL into :new.id from dual;end;";
9、测试
@Test
public void test() {
ApplicationContext ctx=new ClassPathXmlApplicationContext("/applicationContext.xml");
DataSource dataSource=(DataSource) ctx.getBean("dataSource");
JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
IUserService userService=(IUserService) ctx.getBean("userService");
IAdressService addressService=(IAdressService) ctx.getBean("addressService");
/**
* 创建表、序列、主键自增,引擎,之前一直在触发的时候出错
*/
jdbcTemplate.execute(CREATE_ADDRESS_TABLE_SQL);
jdbcTemplate.execute(CREAT_SQU);
jdbcTemplate.execute(KEY_INCRE_SQL);
jdbcTemplate.execute(TRIGGER_SQL);
UserModel user=createDefaultUserModel();
userService.save(user);
}
private UserModel createDefaultUserModel(){
UserModel user=new UserModel();
user.setUser_name("mk");
user.setPassword("000");
AddressModel address=new AddressModel();
address.setProvince("zhejiang");
address.setCity("jinhua");
address.setStreet("hel");
user.setAddress(address);
return user;
}
从Spring容器中获取Service层对象,调用Service层对象持久化对象,大家有没有注意到Spring事务全部在Service层定 义,
为什么会在Service层定义,而不是Dao层定义呢?这是因为在服务层可能牵扯到业务逻辑,而每个业务逻辑可能调用多个Dao
层方法,为保证这些 操作的原子性,必须在Service层定义事务。
Service层的事物管理相当令人头疼,而且是侵入式的,如何消除这些冗长的代码呢,这就需要spring声明式的事物支持。
在实现这一节的时候,我碰到的主要问题是创建表时关于引擎的问题,还有就是如何获得user表中的id然后插入到address表
中,后来我摸索了很久终于知道如何获取自增的主键值了,这了在dao层和service层中一定要搞清楚user表和address中字段
关系。
我参考的博客上面还介绍了事物的属性并举例实现,我还没弄太明白,所以没实现。
下一篇博客中我将展示以下我的声明式事物的实现