Spring--04(Spring的JDBC操作和事務操作)
1.Spring 的 JDBC 的模板
Spring 提供了很多持久層技術的模板類簡化程式設計:
1.使用Spring提供的jdbc模板操作資料庫:首先引入spring-jdbc依賴和mysql的依賴
測試:
public class JDBCDemo1 { @Test public void test1() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://192.168.203.130:3306/springboot?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"); dataSource.setUsername("root"); dataSource.setPassword("******"); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.update("INSERT INTO account(account.`name`,money) VALUES (?,?)", " 會希 ",10000d); } }
配置Spring內建連線池:
<!-- 配置 Spring 的內建連線池 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 配置 JDBC 模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--引入外部的屬性檔案--> <context:property-placeholder location="classpath:jdbc.properties"/>
新建配置檔案:jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.203.130:3306/springboot?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc.username=root
jdbc.password=*****
測試:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestSpring1 { @Autowired private JdbcTemplate jdbcTemplate; @Test public void testorder() { jdbcTemplate.update("INSERT INTO account(account.`name`,money) VALUES (?,?)", " 會希 ",10002d); } }
結果:
2.使用c3p0連線池:引入c3p0的依賴
修改Spring連線池配置檔案:
<!--使用c3p0連線池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
測試:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring1 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testorder()
{
jdbcTemplate.update("INSERT INTO account(account.`name`,money) VALUES (?,?)", " 會希 ",10003d);
}
}
結果:
2.事務
1. 什麼是事務:事務邏輯上的一組操作,組成這組操作的各個邏輯單元,要麼一起成功,要麼一起失敗.
2.事務特性:
原子性 :強調事務的不可分割.
一致性 :事務的執行的前後資料的完整性保持一致.
隔離性 :一個事務執行的過程中,不應該受到其他事務的干擾
永續性 :事務一旦結束,資料就持久到資料庫
3.如果不考慮隔離性引發安全性問題
髒讀 :一個事務讀到了另一個事務的未提交的資料
不可重複讀:一個事務讀到了另一個事務已經提交的 update 的資料導致多次查詢結果不一致
虛幻讀:一個事務讀到了另一個事務已經提交的 insert 的資料導致多次查詢結果不一致.
4.解決讀問題:設定事務隔離級別
未提交讀 :髒讀,不可重複讀,虛讀都有可能發生
已提交讀 :避免髒讀。但是不可重複讀和虛讀有可能發生
可重複讀 :避免髒讀和不可重複讀.但是虛讀有可能發生.
序列化的 :避免以上所有讀問題
Mysql 預設:可重複讀
Oracle 預設:讀已提交
5.Spring 的這組介面是如何進行事務管理:平臺事務管理根據事務定義的資訊進行事務的管理,事務管理的過程中產生一些狀態,將這些狀態記錄到 TransactionStatus 裡面。
5.1PlatformTransactionManager:平臺事務管理器
真正管理事務的物件
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用 Spring JDBC 或 iBatis 進行持久化資料時使用
org.springframework.orm.hibernate3.HibernateTransactionManager 使用Hibernate 版本進行持久化資料時使用
5.2TransactionDefinition:事務定義資訊
事務定義資訊:
* 隔離級別
* 傳播行為
* 超時資訊
* 是否只讀
5.3TransactionStatus:事務的狀態
記錄事務的狀態
事務的傳播行為:
* 保證同一個事務中
PROPAGATION_REQUIRED 支援當前事務,如果不存在 就新建一個(預設)
PROPAGATION_SUPPORTS 支援當前事務,如果不存在,就不使用事務
PROPAGATION_MANDATORY 支援當前事務,如果不存在,丟擲異常
* 保證沒有在同一個事務中
PROPAGATION_REQUIRES_NEW 如果有事務存在,掛起當前事務,建立一個新的事務
PROPAGATION_NOT_SUPPORTED 以非事務方式執行,如果有事務存在,掛起當前事務
PROPAGATION_NEVER 以非事務方式執行,如果有事務存在,丟擲異常
PROPAGATION_NESTED 如果當前事務存在,則巢狀事務執行
理解事物的傳播特性:
如果你在你的Service層的這個方法中,除了呼叫了Dao層的方法之外,還呼叫了本類的其他的Service方法,那麼在呼叫其他的Service方法的時候,這個事務是怎麼規定的呢,我必須保證我在我方法裡掉用的這個方法與我本身的方法處在同一個事務中,否則如果保證事物的一致性。事務的傳播特性就是解決這個問題的
預設情況下當發生RuntimeException的情況下,事務才會回滾,所以要注意一下 如果你在程式發生錯誤的情況下,有自己的異常處理機制定義自己的Exception,必須從RuntimeException類繼承 這樣事務才會回滾!
搭建一個轉賬的環境
建立一個賬戶物件Account
package com.wx.springsource1.pojo;
public class Account {
private String id;
private String name;
private Double money;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
建立一個轉賬介面:
package com.wx.springsource1.service;
public interface AccountService {
//轉賬的方法
public void transfer(String from,String to,Double money);
public void outMoney(String from,Double money);
public void inMoney(String to,Double money);
}
實現轉賬方法:
package com.wx.springsource1.serviceimp;
import com.wx.springsource1.pojo.Account;
import com.wx.springsource1.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
public class AccountServiceImp implements AccountService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void transfer(String from, String to, Double money) {
outMoney(from,money);
inMoney(to,money);
}
//A減少錢
public void outMoney(String from,Double money)
{
Account account = jdbcTemplate.queryForObject("SELECT * FROM account WHERE account.`name`=?", new MyRowMapper(),from);
jdbcTemplate.update("UPDATE account SET `money` = ?-? WHERE `name` = ?",account.getMoney(),money,account.getName());
}
//B賬戶增加錢
public void inMoney(String to,Double money)
{
Account account = jdbcTemplate.queryForObject("SELECT * FROM account WHERE account.`name`=?", new MyRowMapper(),to);
jdbcTemplate.update("UPDATE account SET `money` = ?+? WHERE `name` = ?",account.getMoney(),money,account.getName());
}
}
處理查詢結果集:
package com.wx.springsource1.serviceimp;
import com.wx.springsource1.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MyRowMapper implements RowMapper<Account> {
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getString(1));
account.setName(resultSet.getString(2));
account.setMoney((Double)resultSet.getDouble(3));
return account;
}
}
配置Spring:
<bean id="orderDao" class="com.wx.springsource1.daoimp.OrderDaoImp"/>
測試:
package com.wx.springsource1.test;
import com.wx.springsource1.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTx {
@Autowired
private AccountService accountService;
@Test
public void testtx()
{
accountService.transfer("aaa","bbb",100d);
}
}
加入事務的管理:Spring事務管理有三種實現方式,首先是手動編寫程式碼實現事務管理,然後是xml配置事務管理核心思想史AOP,最後是註解式事務管理,最簡單,也最常用,下面就使用註解式事務管理
1.新增約束檔案,如果不新增,標籤無法識別
2.配置事務管理器,這裡使用Spring-jdbc的事務
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
3.開啟註解式事務
<!--開啟註解管理事務-->
<tx:annotation-driven transaction-manager="transactionManager"/>
4.在使用類或者方法上添加註解 @Transactional