Spring03
Spring jdbc Template
Spring提供了一个JDBC模板,它类似于dbutils工具
如何使用spring.jdbc.template?
第一:要导入相关jar包
第二:在这个基础上我们还需要导入
第三:还需要导入相关的数据库驱动jar包
案例:
1.导入相关jar包
2.创建库与表`
CREATE DATABASE springtest;
CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
sex VARCHAR(20)
)
INSERT INTO t_user VALUES(NULL,'tom',20,'男');
INSERT INTO t_user VALUES(NULL,'fox',30,'男');
INSERT INTO t_user VALUES(NULL,'tony',40,'男');
3.编码
@Test
public void test1(){
//1.创建数据库连接池
DriverManagerDataSource dataSource=new DriverManagerDataSource();
//2.设置dataSource相关的属性(driverclass url username password)
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///springtest");
dataSource.setUsername("root");
dataSource.setPassword("root");
//3.创建一个JdbcTemplate来完成操作
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
jdbcTemplate.execute("update t_user set name='张三' where id=1");//execute方法可以执行任意sql
}
使用spring内置的链接池DriverManagerDataSource
<bean id="driverManagerDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource;">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///springtest"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="driverManagerDataSource"></property>
</bean>
public class JdbcTemplateTest2 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test1(){
//3.创建一个JdbcTemplate来完成操作
jdbcTemplate.execute("update t_user set name='李四' where id=2");//execute方法可以执行任意sql
}
}
C3P0开源链接池配置
1.导入c3p0相关的jar包
2.创建一个ComboPoolDataSource对象,设置相关的属性
<!-- 创建c3p0链接池 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="dirverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///springtest"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
引入外部属性文件
Spring支持经常修改属性,在properties文件中声明,在xml配置文件中引入外部的properties文件的信息
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///springtest
jdbc.username=root
jdbc.password=root
在applicationContext.xml文件中引入
<!-- 引入外部的properties文件 -->
<context:property-placeholder location="classpath:db.properties"/>
在自己配置中需要从properties文件中引入的信息可以使用${}方式来获取
<!-- 创建c3p0链接池 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="dirverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
JdbcTemplate CRUD
只需要使用JdbcTemplate的update方法就可以执行insert update delete操作
jdbcTemplate.update("update t_user set name=? where id=?", "tom", 1);
jdbcTemplate.update("insert into t_user values(null,?,?,?)", "赵六", 30, "男");
jdbcTemplate.update("delete from t_user where id=?", 4);
执行select操作
简单数据返回
// 测试返回简单数据类型
@Test
public void test4() {
String name = jdbcTemplate.queryForObject("select name from t_user where id=?", String.class, 2);
System.out.println(name);
}
// 测试返回简单数据类型
@Test
public void test5() {
Integer count = jdbcTemplate.queryForObject("select count(*) from t_user", Integer.class);
System.out.println(count);
}
复杂数据返回
注意:如果返回一个domain对象,可以使用queryForObject方法,如果返回的是List<?>对象,可以使用query方法,但是都需要使用RowMapper来对ResultSet进行处理
@Test
public void test6(){
User user=jdbcTemplate.queryForObject("select * from t_user where id=?", new RowMapper<User>(){
@Override
public User mapRow(ResultSet arg0, int arg1) throws SQLException {
//完成结果集封装成user对象操作
User user=new User();
user.setAge(arg0.getInt("age"));
user.setId(arg0.getInt("id"));
user.setName(arg0.getString("name"));
user.setSex(arg0.getString("sex"));
return user;
}
},1);
System.out.println(user);
}
//测试返回一个复杂的数据类型-List<User>
@Test
public void test7(){
List<User> user=jdbcTemplate.query("select * from t_user", new RowMapper<User>(){
@Override
public User mapRow(ResultSet arg0, int arg1) throws SQLException {
//完成结果集封装成user对象操作
User user=new User();
user.setAge(arg0.getInt("age"));
user.setId(arg0.getInt("id"));
user.setName(arg0.getString("name"));
user.setSex(arg0.getString("sex"));
return user;
}
});
System.out.println(user);
}
RowMapper它有一个实现类叫BeanPropertyRowMapper
如果使用BeanPropertyRowmapper,实体类必须提供一个无参数的public构造方法,类中的bean属性名称与表中的列要对应
注意:这个类是在spring2.0后提供
//使用BeanPropertyRowMapper来完成结果集到Bean封装
@Test
public void test8(){
User user=jdbcTemplate.queryForObject("select * from t_user where id=?",new BeanPropertyRowMapper<User>(User.class),1);
System.out.println(user);
}
Spring事务管理
案例:转账操作
1.创建一个账户表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money DOUBLE
)
INSERT INTO account VALUES(NULL,'tom',1000);
INSERT INTO account VALUES(NULL,'fox',1000);
关于Service与dao的配置
<!-- service -->
<bean id="accountService" class="com.baidu.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.baidu.dao.AccountDAOImpl">
<!-- 当注入dataSource后,底层会自动创建一个jdbcTemplate -->
<property name="dataSource" ref="c3p0DataSource"/>
</bean>
我们让dao去extends jdbcDaoSupport类,这个类中它创建了JdbcTempate,前提是我们需要注入一个dataSource
Sping事务管理机制
Spring事务管理的四个优点:
1.提供一致的对于不同的事务管理的API
2.支持声明式事务管理
3.编程事务管理
4.优秀的整合与Spring的数据访问
Spring事务管理主要提供了三个接口来完成
- org.springframework.transaction.PlatformTransactionManager
这是一个事务管理器,可以来选择相关的平台(jdbc hibernate jpa…) - TransactionDefinition
它定义事务的一些相关信息 例如 隔离 传播 超时 只读 - TransactionStatus
它主要描述事务具体的运行状态
PlatformTransactionManager
平台事务管理器
在不同的持久化层解决技术它的事务代码不一样
JDBC开发
Connection con=……;
con.setAutoCommit(false);//开启事务
con.rollback();
con.commit();
Hibernate开发
Session session=….;
Transaction t=session.beginTransaction();
t.commit();
t.rollback();
PlatformTransactionManager接口API
TransactionDefinition
它描述的是事务的定义信息
- 隔离
- 传播
- 超时
- 只读状态
事务的四个特性ACID原子性 一致性 隔离性 持久性
不考虑事务隔离性有什么问题?
脏读、不可重复读、虚读。
ISOLATION_DEFUALT它使用后端数据库的默认隔离级别(spring中选项)
ISOLATION_READ_UNCOMMITTED不能解决问题,会发生脏读、不可重复读、虚读
ISOLATION_READ_COMMITTED可以解决脏读会产生不可重复读与虚读。
ISOLATON_REPEATABLE_READ可以解决脏读,不可重复读、解决不了虚读
ISOLATION_SERIALIZABLE:串行化,可以解决所有问题
对于不现的数据库,它的底层默认事务隔离级别不一样
Oracle数据库它默认的是read_committed
Mysql数据库它默认的是repeatable_read
超时:
默认值是-1它使用的是数据库默认的超时时间
只读:
它的值有两个true/false,如果选择true一般是在我们select操作时
传播:
它解决的是两个被事务管理的方法相互调用问题。它与数据库没关系,是程序内部维护的问题。
PROPAGATION REQUIRED:默认值 两个操作处于同一个事务,如果之前没有事务,新建一个事务
PROPAGATION REQUIRES NEW:两个操作处于不同的事务
PROPAGATION NESTED:它是一种嵌套事务,它是使用SavePoint来实现的。事务回滚时可以回滚到指定的savepoint注意:它只对DataSourceTransactionManager有作用
一下不是经常使用的
PROPAGATION SUPPORTS支持当前事务,如果不存在,就不使用事务
PROPAGATION MANDATORY支持当前事务,如果不存在,抛出异常
PROPAGATION NOT SUPPORTED以非事务运行,如果有事务不存在,挂起当前事务
PROPAGATION NEVER以非事务运行,如果有事务存在,抛出异常
TransactionStatus
它定义了事务状态信息,在事务运行过程中,得到某个时间点的状态
声明事务管理
事务管理方式
1.编码方案 不建议使用,它具有入侵性。在原有的业务代码基础上去添加事务管理代码
2.声明事务控制,基于AOP对目标进行代理,添加around环绕通知。
这种方案,它不具有侵入性,不需要修改原来的业务代码
基于xml配置声明事务管理方案
第一步:在applicationContext.xml文件中添加aop与tx的名称空间
<?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">
第二步:在applicationContext.xml文件中配置
Spring提供的advice是传统的spring advice
1.声明事务管理
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="c3p0DataSource"></property>
</bean>
2.配置通知
Spring为我们提供了一个TransactionInterceptor来完成增强
public class TransactionInterceptor extends TransactionAspectSuppot implements MethodInterceptor, Serializable{}
对于这个增强,我们可以使用spring为我们提供的一个标签< tx:advice>来完成操作
<!-- 配置通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- name:必须的,对哪些方法进行事务控制
isolation可选设置事务隔离级别默认DEFAULT
propagation:可选 设置事务传播 默认值REQUIRED
timeout 可选 超时时间默认值-1
read-only 可选默认值是false 如果不是只读,它可以对insert update delete操作,如果是只读不可以。
rollback-for可选可以设置一个异常,如果产生这个异常,触发事务回滚
no-rollback-for 可选可以设置一个异常,如果产生这个异常,不会触发事务回滚
-->
<tx:method name="account"/>
</tx:attributes>
</tx:advice>
3.配置切面
因为使用的是传统的spring的advice,需要使用< aop:advisor>
<!-- 配置切面 -->
<aop:config>
<aop:pointcut expression="execution(*com.ithei.service.IAccountService.account(..))" id="txPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
基于annotation声明式事务管理
可以使用@Transaction来在类或方法上添加声明式事务管理
注意:需要在applicationContext.xml文件中使用
<tx:annotation-driven transaction-manager="transactionManager"/>
相当于开启注解事务控制
问题:关于xml方式与annotation方式的优缺点?
从简单上来说,使用注解更方便。
使用配置的方案,可以对事务配置进行集中维护。
<tx:method name="get*">
<tx:method name="save*">