Spring JDBC模板
在使用普通的 JDBC 数据库时,需要写代码来处理异常,打开和关闭数据库连接等。但Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。当从数据库中获取数据时,你所需要做的只是定义连接参数,指定要执行的 SQL 语句。
Spring框架提供了JDBC模板模式,即JdbcTemplate
,简化许多代码,但在实际应用中jdbcTemplate并不常用。更多的时候,用的是Hibernate框架和MyBatis
框架进行数据库编程。
1.1 JdbcTemplate 配置
- 在配置文件中需要引入上下文约束、aop约束、tx约束。
- 注册DriverManagerDataSource类作为连接池(spring默认的连接池,如果使用其他连接池可以修改注册的bean类);在属性中设置驱动类,数据库连接地址,用户名和密码(其他连接设置可参照JDBC)。
- 注册springJDBC的核心类JdbcTemplate,在使用中就可以实例化该bean创建数据库连接。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<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/selection_course"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
复制代码
1.2 JdbcTemplate 常用方法
public int update(String sql, Object[] args)
返回值为int类型,代表影响的行数,sql是需要执行的SQL语句,如果需要提供参数可以使用?来占位,args是需要向SQL语句中传递的参数,以数组的形式传递。
String insertSql = "insert into user values(null,?,?)";
Object param1[] = {"chenheng1", "男"};
jdbcTemplate.update(sql, param1);
复制代码
对数据表进行查询操作,rowMapper将结果集映射到用户自定义的类中,实际应用中常用BeanPropertyRowMapper
,自定义类中的属性要与数据表的字段对应。
1.3 JdbcTemplate实例
public List<T> query (String sql, RowMapper<T> rowMapper, Object args[])
- 创建数据库及对应的po类
MyUser.java需要对应的setter和getter,否则在select的时候无法正确赋值
package com.pojo;
public class MyUser {
private Integer uid;
private String uname;
private String usex;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUsex() {
return usex;
}
public void setUsex(String usex) {
this.usex = usex;
}
public String toString() {
return "myUser [uid=" + uid +", uname=" + uname + ", usex=" + usex + "]";
}
}
复制代码
- 在XML中配置bean JdbcTemplate,dataSource是数据池类,需要配置的属性包括驱动类,URL,用户名和用户密码。将dataSource注入JdbcTemplate中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<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/sql_springtest?characterEncoding=utf8&useSSL=false" />
<property name="username" value="root"/>
<property name="password" value="20011017lh"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
复制代码
- 测试类
TestJdbc.java,在测试代码中使用了@Test单元测试,这个测试默认是不支持注解注入的,需要加上
@RunWith(SpringJUnit4ClassRunner.class)//SpringJUnit支持,由此引入Spring-Test框架支持!
@ContextConfiguration(locations="classpath:applicationContext.xml")//加载
复制代码
package com.dao;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.pojo.MyUser;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)//SpringJUnit支持,由此引入Spring-Test框架支持!
@ContextConfiguration(locations="classpath:applicationContext.xml")//加载applicationContext.xml
@Repository("testJdbc")
public class TestJdbc {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void select() {
String sql = "select * from user where uid = ? and uname = ?";
RowMapper<MyUser> rowMapper = new BeanPropertyRowMapper<MyUser>(MyUser.class);
Object[] ids = new Object[]{2,"王小虎"};
List<MyUser> ans = jdbcTemplate.query(sql,rowMapper,ids);
System.out.println(Arrays.toString(ans.toArray()));
}
@Test
public void update() {
String sql = "insert into user values(null, ?, ?)";
Object[] params = {"王中虎", "男"};
int ans = jdbcTemplate.update(sql,params);
System.out.println(ans);
}
}
复制代码
Spring 事务管理 – 编程式
事务:必须连续执行的一系列操作,Spring中有多种方式对事物进行管理。
2.1 事务管理器
在代码中显式调用beginTransaction()
、commit()
、rollback()
等事务处理相关的方法,就是编程式事务管理,过编程的方式来进行事务处理。当只有少数事务操作时,编程式事务管理才比较合适。
-
PlatformTransactionManager
Spring的事务基础结构的中心接口,常用实现类
DataSourceTransactionManager
:使用JDBC管理事务HibernateTransactionManager
:使用Hibernate管理事务 -
-
TransactionDefinition
Spring的事务定义,用于定义事务相关的信息。例如,隔离级别、传播行为、是否只读、超时信息等
TransactionStatus
用于记录事务管理的过程中,事务的状态信息
Spring进行事务管理时,PlatformTransactionManager根据TransactionDefinition进行事务的管理,这个过程中产生各种状态信息将记录到TransactionStatus中
实例:
- 在XML中配置DataSourceTransactionManager的bean,他与JDBC模板配合使用,JDBC模板进行数据库操作,DataSourceTransactionManager对事务进行管理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置数据源 -->
<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/sql_springtest?characterEncoding=utf8&useSSL=false" />
<property name="username" value="root"/>
<property name="password" value="20011017lh"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
复制代码
- 测试使用事务管理器进行事务管理
package com.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
@Repository("codeTransaction")
public class CodeTransaction {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private DataSourceTransactionManager txManager;
@Test
public void test() {
TransactionDefinition tf = new DefaultTransactionDefinition(); // 默认事务定义,如隔离级别、传播行为等
TransactionStatus ts = txManager.getTransaction(tf);
String message = "执行成功,没有事务回滚";
try {
String sql1 = " insert into user values(?,?,?) ";
Object param[] = { 1, "陈恒", "男" };
jdbcTemplate.update(sql1, param); // 添加一条数据
jdbcTemplate.update(sql1, param); // 添加相同的一条数据,使主键重复
// 提交事务
txManager.commit(ts);
} catch (Exception e) {
txManager.rollback(ts); //由事务管理器进行回滚
message = "事务回滚";
//e.printStackTrace();
}
System.out.println(message);
}
}
复制代码
2.2 事务管理器模板
不管是JDBC模板还是事务管理模板都是为了简化代码,使用模板方法设计模式对原始JDBC/事务管理方式进行封装。
TransactionTemplate
TransactionTemplate的execute()
方法有一个TransactionCallback
接口类型的参数,该接口中定义了一个doInTransaction()
方法,通常以匿名内部类的方式实现TransactionCallback 接口,并在其doInTransaction()方法中书写业务逻辑代码。
这里可以使用默认的事务提交和回滚规则,在业务代码中不需要显式调用任何事务处理的API。doInTransaction()方法有一个TransactionStatus
类型的参数,可以在方法的任何位置调用该参数的setRollbackOnly()
方法将事务标识为回滚,以执行事务回滚。
根据默认规则,如果在执行回调方法的过程中抛出了未检查异常,或者显式调用了setRollbackOnly()方法,则回滚事务;如果事务执行完成或者抛出了checked类型的异常,则提交事务。
基本结构
public Object getObject(String str) {
/*
* 执行带有返回值<Object>的事务管理
*/
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
try {
...
//....... 业务代码
return new Object();
} catch (Exception e) {
//回滚
transactionStatus.setRollbackOnly();
return null;
}
}
});
}
复制代码
实例:
- XML,添加事务管理器模板
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置数据源 -->
<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/sql_springtest?characterEncoding=utf8&useSSL=false" />
<property name="username" value="root"/>
<property name="password" value="20011017lh"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 为事务管理器txManager创建transactionTemplate -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager" />
</bean>
</beans>
复制代码
- 测试
package com.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
@Repository("transactionTemplateDao")
public class TransactionTemplateDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private TransactionTemplate transactionTemplate;
String message = "";
@Test
public void test() {
// 以匿名内部类的方式实现TransactionCallback 接口,使用默认的事务提交和回滚规则,在业务代码中不需要显式调用任何事务处理的API
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus ts) {
String sql1 = " insert into user values(?,?,?) ";
Object param[] = { 2, "陈恒", "男" };
try {
jdbcTemplate.update(sql1, param);
jdbcTemplate.update(sql1, param);
message = "执行成功,没有事务回滚";
} catch (Exception e) {
message = "主键重复,事务回滚";
//e.printStackTrace();
ts.setRollbackOnly();
}
return message;
}
});
System.out.println(message);
}
}
复制代码
Spring 事务管理 – 声明式
Spring的声明式事务管理,是通过AOP技术实现的事务管理,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务管理最大的优点是不需要通过编程的方式管理事务,因而不需要在业务逻辑代码中掺杂事务处理的代码,只需相关的事务规则声明,便可以将事务规则应用到业务逻辑中。通常情况下,在开发中使用声明式事务处理,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大方便后期的代码维护。
采用AOP实现,所以可以使用XML和注解两种方式来实现
3.1 基于XML
基于XML方式的声明式事务管理是通过在配置文件中配置事务规则
的相关声明来实现的。
Spring框架提供了tx命名空间来配置事务,<tx:advice>
元素来配置事务的通知。
配置<tx:advice>
元素需指定id和transaction-manager属性,id属性是配置文件的唯一标识,transaction-manager属性指定事务管理器。
<tx:attributes>
子元素可配置多个<tx:method>
子元素指定执行事务的细节。
当<tx:advice>
元素配置了事务的增强处理后,就可以通过编写AOP配置,让Spring自动对目标对象生成代理。
创建了Dao、Service和Controller三层,,具体实现步骤如下:
- 在XML中配置JDBC模板连接数据库
<!-- 配置数据源 -->
<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/sql_springtest?characterEncoding=utf8&useSSL=false" />
<property name="username" value="root"/>
<property name="password" value="20011017lh"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
复制代码
- 建立Dao层并添加注解,Dao层通过jdbc模板操作数据库
package com.dao;
@Repository("testDao")
public class TestDaoImpl implements TestDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int save(String sql, Object[] param) {
return jdbcTemplate.update(sql,param);
}
@Override
public int delete(String sql, Object[] param) {
return jdbcTemplate.update(sql,param);
}
}
复制代码
- 建立Service层并添加注解,Service层通过Dao层提供的方法向控制层提供save和delete方法
package com.service;
import com.dao.TestDao;
@Service("testService")
public class TestServiceImpl implements TestService{
@Autowired
private TestDao testDao;
@Override
public int save(String sql, Object[] param) {
return testDao.save(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return testDao.delete(sql, param);
}
}
复制代码
- 建立Controller层并添加注解(这里多了个注解
@Transactional
),通过服务层提供的方法,编写test方法测试一个由三个操作组成的事务
package com.controller;
import com.service.TestService;
@Controller("statementController")
public class StatementController {
@Autowired
private TestService testService;
public String test() {
int i=0;
String message = "";
String deleteSql ="delete from user where id = 2";
String saveSql = "insert into user values(?,?,?)";
Object param[] = {1222,"chenheng1","男"};
try{
i=testService.delete(deleteSql, null);
i=testService.save(saveSql, param);
i=testService.save(saveSql, param);
}catch(Exception e){
message = "主键重复,事务回滚!"+e.getClass()+" ,i = "+i;
//e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return message;
}
}
复制代码
- 在XML中添加注解扫描
<context:component-scan base-package="com"/>
复制代码
- 在XML中添加事务管理,为事务管理器创建切面类,通过AOP将切面类与切入点相连
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 编写通知声明事务-->
<tx:advice id="myAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- * 表示任意方法 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- 编写AOP,让Spring自动对目标对象生成代理,需要使用AspectJ的表达式 -->
<aop:config>
<!-- 定义切入点 对控制层的所有类的所有方法,都执行切入-->
<aop:pointcut id="txPointCut" expression="execution(* com.controller.*.*())"/>
<!-- 切面:将切入点与通知关联 -->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>
</aop:config>
复制代码
3.2 基于注解
@Transactional
作用于接口、接口方法、类以及类方法上,但是Spring小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
当作用于类上时,该类的所有public方法将都具有该类型的事务属性,如果在方法级别使用该注解将覆盖类级别的定义。
示例也是建立Dao,Service,Controller,有两方面不同
- XML配置文件中开启扫描注解,配置JDBC模板,配置事务管理器,开启注解事务
<!-- 指定需要扫描的包(包括子包),使注解生效 -->
<context:component-scan base-package="com"/>
<!-- 配置数据源 -->
<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/sql_springtest?characterEncoding=utf8&useSSL=false" />
<property name="username" value="root"/>
<property name="password" value="20011017lh"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 编写通知声明事务,即切面类 -->
<tx:advice id="myAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- * 表示任意方法 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- 为事务管理器注册注解驱动 -->
<tx:annotation-driven transaction-manager="txManager" />
复制代码
- 在Spring MVC中,通常通过Service层进行事务管理,因此需要为Service层添加@Transactional注解。
@Service("testService")
@Transactional
//加上注解@Transactional,就可以指定这个类需要受Spring的事务管理
//注意@Transactional只能针对public属性范围内的方法添加
public class TestServiceImpl implements TestService{
@Autowired
private TestDao testDao;
@Override
public int save(String sql, Object[] param) {
return testDao.save(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return testDao.delete(sql, param);
}
}
复制代码