一、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或者註解方式的聲明事務管理後,出現異常,則“扣錢”與“收款”操作都不能執行了,從而實現了事務管理。