一、Spring的JDBC模板的使用
1.使用入门
需要引入的jar包:
基本的jar包:
spring-expression-4.2.4.RELEASE.jar
spring-core-4.2.4.RELEASE.jar
spring-context-4.2.4.RELEASE.jar
spring-beans-4.2.4.RELEASE.jar
spring-test-4.2.4.RELEASE.jar
com.springsource.org.apache.log4j-1.2.15.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
数据库驱动jar包:
mysql-connector-java-5.1.7-bin.jar
Spring中JDBC模板的jar包:
spring-tx-4.2.4.RELEASE.jar
spring-jdbc-4.2.4.RELEASE.jar
测试代码:
public class jdbcdemo1 {
@Test
public void demo1() {
//创建连接池
DriverManagerDataSource datasource = new DriverManagerDataSource();
datasource.setDriverClassName("com.mysql.jdbc.Driver"); //驱动类在导入的jar文件中
datasource.setUrl("jdbc:mysql:///jdbc_database"); //jdbc_database为所创建的数据库
datasource.setUsername("root");
datasource.setPassword("2e5y8hxf");
//创建jdbc模板
JdbcTemplate jdbctemplate = new JdbcTemplate(datasource);
jdbctemplate.update("insert into account values(null,'张三丰',10000)");
}
}
注意:这里"com.mysql.jdbc.Driver"路径是在mysql-connector-java-5.1.7-bin.jar包中的com/mysql/jdbc目录下的Driver.class文件。
终端SQL操作:
create database jdbc_database;
use jdbc_database;
mysql> create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
select *from account;
测试结果:
2.将连接池和模板交给Spring管理
导入jar包:
需要另外导入spring-aop-4.2.4.RELEASE.jar
配置文件代码:
<?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">
<!-- 配置SPring内置的连接池 -->
<bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value = "com.mysql.jdbc.Driver"/>
<property name="url" value = "jdbc:mysql:///jdbc_database"/>
<property name="username" value = "root"/>
<property name="password" value = "2e5y8hxf"/>
</bean>
<!-- 配置Spring的JDBC模板 -->
<bean id = "jdbctemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref = "dataSource"/>
</bean>
</beans>
注意的问题:注意配置文件要建在src文件下,不能建在包下。
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:jdbc_first.xml")
public class jdbcdemo1 {
@Resource(name="jdbctemplate")
private JdbcTemplate jdbctemplate;
@Test
public void demo1() {
jdbctemplate.update("insert into account values(null,?,?)","张无忌",100000);
}
}
3.DBCP连接池的配置
导入jar包:
com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
com.springsource.org.apache.commons.pool-1.5.3.jar
配置文件代码:
将2.中连接池部分配置代码替换为DPCP连接池部分代码即可
<!-- 配置DPCP连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value = "com.mysql.jdbc.Driver"/>
<property name="url" value = "jdbc:mysql:///jdbc_database"/>
<property name="username" value = "root"/>
<property name="password" value = "2e5y8hxf"/>
</bean>
4.C3P0连接池的配置
导入jar包:
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
配置文件代码:
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value = "com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value = "jdbc:mysql:///jdbc_database"/>
<property name="user" value = "root"/>
<property name="password" value = "2e5y8hxf"/>
</bean>
5.引入外部属性文件
建立属性文件:txt文件,包含以下内容
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///jdbc_database
jdbc.username=root
jdbc.password=2e5y8hxf
引入属性文件的两种方式:
<!-- 第一种方式:通过bean标签引入(使用较少) -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"/>
</bean>
<!-- 第二种方式:通过context标签引入-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 修改连接池:配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value = "${jdbc.driverClass}"/>
<property name="jdbcUrl" value = "${jdbc.url}"/>
<property name="user" value = "${jdbc.username}"/>
<property name="password" value = "${jdbc.password}"/>
</bean>
6.进行增删改查的操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:jdbc_first.xml")
public class jdbcdemo1 {
@Resource(name="jdbctemplate")
private JdbcTemplate jdbctemplate;
@Test
//添加一列数据
public void demo1() {
jdbctemplate.update("insert into account values(null,?,?)","杨过",100000);
}
@Test
//修改id为3的一列数据
public void demo2() {
jdbctemplate.update("update account set name = ? ,money = ? where id = ?","任我行",999999,3);
}
@Test
//删除id为2的一列数据
public void demo3() {
jdbctemplate.update("delete from account where id = ?",2);
}
@Test
//查询id为1的name数据
public void demo4() {
String abc = jdbctemplate.queryForObject("select name from account where id = ?", String.class,1);
System.out.println(abc);
//格式:jdbctemplate.queryForObject(sql, rowMapper, args);
}
@Test
//对查询结果进行数值统计
public void demo5() {
Long count = jdbctemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(count);
//格式:jdbctemplate.queryForObject(sql, requiredType)
}
@Test
//将查询到的结果封装到一个对象中
public void demo6() {
Account account = jdbctemplate.queryForObject("select *from account where id = ?", new MyRowMapper(),8);
System.out.println(account);
//格式:jdbctemplate.queryForObject(sql, requiredType)
}
@Test
//查询多条记录
public void demo7() {
List<Account> list = jdbctemplate.query("select *from account", new MyRowMapper());
for(Account account:list) {
System.out.println(account);
}
}
class MyRowMapper implements RowMapper<Account>{
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
return account;
}
}
}
二、Spring的事务处理
1.事务管理概述
事务管理详解:转自:https://www.cnblogs.com/yixianyixian/p/8372832.html
什么是事务?
事务:逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
举例:取钱。
比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;然后ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个取钱过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。
事务的特性:
原子性:事务不可分割;
一致性:事务执行前后,数据完整性保持一致;
隔离性:一个事务的执行不应该受到其他事务的干扰;
持久性:一旦事务结束,数据就持续化到数据库。
如果不考虑隔离性应发的安全性问题:
读问题:
脏读:一个事务读到另一个事务未提交的数据;
不可重复读:一个事务读到另一个事务已经提交的update的数据,导致一个事务中多次查询结果不一致;
虚读、幻读:一个事务读到另一个事务已经提交的insert的数据,导致一个事务中多次查询结果不一致;
写问题:
丢失更新
解决读问题:
设置事务的隔离级别:
Read uncommitted:未提交读,任何读问题解决不了;
Read committed:已提交读,解决脏读,但是不可重复读,并且虚读有可能发生;
Repeatable read:重复读,解决脏读和不可重复读,但是虚读有可能发生;
Serializable:解决所有读问题(效率较低)。
2.Spring的事务管理API
PlatformTransactionManager:平台事务管理器,这是一个接口,是Spring用于管理事务的真正的对象,其具体实现类如下:
DataSourceTransactionManager :底层使用JDBC管理事务;
HibernateTransactionManager :底层使用Hibernate管理事务;
TransactionDefinition:事务定义信息,用于定义事务相关的信息,隔离级别、超时信息、传播行为、是否只读。
TransactionStatus:事务的状态,用于记录在事务管理过程中,事务的状态的对象。
事务管理API之间的关系:Spring进行事务管理的时候,首先,平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中产生各种状态,将这些状态的信息记录到事务的状态的对象中。
3.Spring的事务传播行为
事务传播行为解决的问题:如果遇到了特别复杂的业务逻辑,有可能出现业务层之间的方法相互调用,事务传播就是解决业务层方法相互调用的问题。
举例:如下图所示,是一个业务层之间方法相互调用的情况
Spring中提供了7中事务传播行为:
保证多个操作在同一事务中:
PROPAGATION_REQUIRED :默认值,如果X中有事务,使用X中的事务,如果X中没有事务,则创建一个新的事务,将操作(即X()、c()、d()三个操作)包含进来。
PROPAGATION_SUPPORTS :支持事务,如果X中有事务,则使用X中的事务;如果X中没有事务,则不使用事务。
PROPAGATION_MANDATORY:如果X中有事务,则使用X中的事务;如果X中没有事务,则抛出异常。
保证多个操作不再同一个事务中:
PROPAGATION_REQUIRES_NEW:如果X中有事务,将X中的事务挂起(暂停),并创建新事务,只包含自身操作(即c、d);如果X中没有事务,也创建一个新事务,包含自身操作。
PROPAGATION_NOT_SUPPORTED:如果X中有事务,将X中的事务挂起,不使用事务管理。
PROPAGATION_NEVER:如果A中有事务,报异常。
嵌套式事务:
PROPAGATION_NESTED:嵌套事务,如果X中有事务,按照X中的事务执行,执行完后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。
4.搭建Spring事务管理环境
步骤:
创建Service接口和实现类、
创建DAO接口和实现类、
配置Service和DAO将其交给Spring管理
代码:
Service和DAO接口和实现类:
public interface AccountService {
public void transfer(String from,String to,double money);
}
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/*
* from:转出账户
* to:转入账户
* money:金额
* */
@Override
public void transfer(String from, String to, double money) {
accountDao.outMoney(from, money);
accountDao.inMoney(to, money);
}
}
public interface AccountDao {
public void outMoney(String from,double money);
public void inMoney(String to, double money);
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
/* 这里继承了JdbcDaoSupport之后,就不需要使用这部分代码了,因为JdbcDaoSupport 里包含get与set方法
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
*/
@Override
public void outMoney(String from,double money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);
}
@Override
public void inMoney(String to,double money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);
}
}
配置文件代码:
<?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">
<!-- 配置Service -->
<bean id = "accountService" class = "package2.AccountServiceImpl">
<property name="accountDao" ref = "accountDao"/>
</bean>
<!-- 配置DAO -->
<bean id = "accountDao" class = "package2.AccountDaoImpl">
<property name="jdbcTemplate" ref = "jdbcTemplate"/>
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value = "${jdbc.driverClass}"/>
<property name="jdbcUrl" value = "${jdbc.url}"/>
<property name="user" value = "${jdbc.username}"/>
<property name="password" value = "${jdbc.password}"/>
</bean>
<!-- 配置Spring的JDBC模板(这一步可以通过一些操作省略) -->
<bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref = "dataSource"/>
</bean>
</beans>
测试类代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx.xml")
public class spring_demo1 {
@Resource(name = "accountService")
private AccountService accountService;
@Test
public void demo1() {
accountService.transfer("张三丰", "张无忌", 500);
}
}
测试结果:张三丰给张无忌转了500块钱
5.编程式事务管理(需要写代码)
第一步:配置平台事务管理器
<!-- 配置平台事务管理器 -->
<bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref = "dataSource"/>
</bean>
第二步:配置事务管理的模板类
<!-- 配置事务管理的模板 -->
<bean id = "transactionTemplate" class = "org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref = "transactionManager"/>
</bean>
第三步:在业务层注入事务管理的模板
<!-- 配置Service -->
<bean id = "accountService" class = "package2.AccountServiceImpl">
<property name="accountDao" ref = "accountDao"/>
<!-- 注入事务管理的模板 -->
<property name="transactionTemplate" ref = "transactionTemplate"/>
</bean>
同时在AccountServiceImpl类中添加如下代码:
//注入事务管理的模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
第四步:编写事务管理的代码
在AccountServiceImpl实现类中修改成如下代码:
@Override
public void transfer(String from, String to, double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
accountDao.outMoney(from, money);
int a = 1/0;
accountDao.inMoney(to, money);
}
});
}
6.声明式事务管理(不需要修改代码)
声明式事务管理有两种实现方式:XML方式和注解方式
XML方式:
第一步:搭建事务管理环境(如:4.搭建Spring事务管理环境);
第二步:引入AOP的开发包
spring-aop-4.2.4.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
org.apache.servicemix.bundles.aopalliance-1.0-1.0.0-rc1.jar
第三步:添加配置文件代码:在(4.搭建Spring事务管理环境)中的配置文件添加如下代码,
<!-- 配置平台事务管理器 -->
<bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref = "dataSource"/>
</bean>
<!-- 配置事务的增强 -->
<tx:advice id = "txAdvice" transaction-manager = "transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation = "REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- AOP配置 -->
<aop:config>
<aop:pointcut expression="execution(* package3.AccountServiceImpl.*(..))" id="pointcut1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref = "pointcut1"/>
</aop:config>
注解方式:
第一步:搭建事务管理环境(如:4.搭建Spring事务管理环境);
第二步:引入AOP的开发包
第三步:添加配置文件代码
<!-- 配置平台事务管理器 -->
<bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref = "dataSource"/>
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager = "transactionManager"/>
第四步:在业务层添加注解
结果分析:在进行XML或者注解方式的声明事务管理后,出现异常,则“扣钱”与“收款”操作都不能执行了,从而实现了事务管理。