1. 程式人生 > 其它 >Day57(spring註解高階 事務)57

Day57(spring註解高階 事務)57

第三部分:Spring中的JDBC和事務

一、Spring中對的Jdbc封裝

1、JdbcTemplate

1.1、概述

1.1.1、基本介紹

Spring對資料庫的操作在jdbc上面做了基本的封裝,讓開發者在操作資料庫時只需關注SQL語句和查詢 結果處理器,即可完成功能(當然,只使用JdbcTemplate,還不能擺脫持久層實現類的編寫)。

在配合spring的IoC功能,可以把DataSource註冊到JdbcTemplate之中。同時利用spring基於 aop的事務即可完成簡單的資料庫CRUD操作。

JdbcTemplate的限定命名為org.springframework.jdbc.core.JdbcTemplate。要使用 JdbcTemlate需要匯入spring-jdbc和spring-tx兩個座標。

1.1.2、原始碼

/**
* JdbcTemplate實現了JdbcOperations介面,操作方法都定義在此介面中
*/
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
/**
* 使用預設建構函式構建JdbcTemplate
*/
public JdbcTemplate() {
}
/**
* 通過資料來源構建JdbcTemplate
*/
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
/**
* 當使用預設建構函式構建時,提供了設定資料來源的方法
*/
public void setDataSource(@Nullable DataSource dataSource) {
this.dataSource = dataSource;
}
}

1.1.3、方法說明

execute方法: 可以用於執行任何SQL語句,一般用於執行DDL語句;

update方法及batchUpdate方法: update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語 句;

query方法及queryForXXX方法: 用於執行查詢相關語句;

call方法: 用於執行儲存過程、函式相關語句。

1.2、入門案例

1.2.1、匯入座標

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

1.2.2、編寫實體類

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class Account implements Serializable{
private Integer id;
private String name;
private Double money;
public Integer getId() {
public void setId(Integer 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;
}
}

1.2.3、編寫配置類

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
@Bean
public DataSource createDataSource(){
DriverManagerDataSource dataSource = new
DriverManagerDataSource(url,username,password);
dataSource.setDriverClassName(driver);
return dataSource;
}
}

1.2.4、編寫配置檔案

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
jdbc.username=root
jdbc.password=1234

1.2.5、測試方法

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testSave(){
jdbcTemplate.update("insert into
account(name,money)values(?,?)","ccc",1234f);
}
@Test
public void testUpdate(){
jdbcTemplate.update("update account set name=?,money=? where
id=?","ccc",2345f,1);
}
@Test
public void testDelete(){
jdbcTemplate.update("delete from account where id = ?",1);
}
@Test
public void testFindOne(){
// List<Account> accounts = jdbcTemplate.query("select * from account
where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
Account account = jdbcTemplate.queryForObject("select * from account
where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
System.out.println(account);
@Test
public void testFindAll(){
List<Account> accounts = jdbcTemplate.query("select * from account ",new
BeanPropertyRowMapper<Account>(Account.class));
for(Account account : accounts){
System.out.println(account);
}
}
@Test
public void testFindCount(){
Long count = jdbcTemplate.queryForObject("select count(*) from account
where money > ?",Long.class,999d);
System.out.println(count);
}
@Test
public void testQueryForList(){
// List<Map<String,Object>> list = jdbcTemplate.queryForList("select *
from account where money > ?",999f);
// for(Map<String,Object> map : list){
// for(Map.Entry<String,Object> me : map.entrySet()) {
// System.out.println(me.getKey()+","+me.getValue());
// }
// }
List<Double> list = jdbcTemplate.queryForList("select money from
account where money > ?", Double.class,999f);
for(Double money : list){
System.out.println(money);
}
}
@Test
public void testQueryForMap(){
Map<String,Object> map = jdbcTemplate.queryForMap("select * from account
where id = ?", 2);
for(Map.Entry me:map.entrySet()){
System.out.println(me.getKey()+","+me.getValue());
}
}
@Test
public void testQueryForRowSet(){
SqlRowSet rowSet = jdbcTemplate.queryForRowSet("select * from account
where money > ?",999d);
System.out.println(rowSet);
if(rowSet instanceof ResultSetWrappingSqlRowSet){
while(rowSet.next()) {
ResultSetWrappingSqlRowSet resultSetWrappingSqlRowSet =
(ResultSetWrappingSqlRowSet) rowSet;
String name = resultSetWrappingSqlRowSet.getString("name");
System.out.println(name);
}
}
}
}

1.3.、LobHandler和LobCreator

1.3.1、準備環境

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class Userinfo implements Serializable {
private Integer id;
private byte[] images;
private String description;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public byte[] getImages() {
return images;
}
public void setImages(byte[] images) {
this.images = images;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Userinfo{" +
"id=" + id +
", images=" + Arrays.toString(images) +
", description='" + description + '\'' +
'}';
}
}
/**
* 在JdbcConfig中加入如下程式碼
*/
@Bean
public LobHandler createLobHandler(){
return new DefaultLobHandler();
}

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private LobHandler lobHandler;
@Test
public void testWrite(){
try {
FileSystemResource res = new FileSystemResource
("C:\\Users\\zhy\\Desktop\\6.jpg");
byte[] mockImg = FileCopyUtils.copyToByteArray(res.getFile());
Userinfo userinfo = new Userinfo();
userinfo.setId(3);
userinfo.setImages(mockImg);
userinfo.setDescription("Spring對資料庫的操作在jdbc上面做了基本的封裝,讓開
發者在操作資料庫時只需關注SQL語句和查詢結果處理器,即可完成功能(當然,只使用JdbcTemplate,還
不能擺脫持久層實現類的編寫)。\t\n" +
"\t在配合spring的IoC功能,可以把DataSource註冊到JdbcTemplate之
中。同時利用spring基於aop的事務即可完成簡單的資料庫CRUD操作。\n" +
" JdbcTemplate的限定命名為
org.springframework.jdbc.core.JdbcTemplate。要使用JdbcTemlate需要匯入spring-jdbc和
spring-tx兩個座標。");
jdbcTemplate.execute("insert into
userinfo(id,image,description)values(?,?,?)", new
AbstractLobCreatingPreparedStatementCallback(lobHandler) {
@Override
protected void setValues(PreparedStatement ps, LobCreator
lobCreator) throws SQLException, DataAccessException {
ps.setInt(1,3);
lobCreator.setBlobAsBytes(ps, 2,userinfo.getImages() );
lobCreator.setClobAsString(ps,3,userinfo.getDescription());
}
});
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void testRead(){
// List list = jdbcTemplate.query("select * from userinfo where
id=?",new BeanPropertyRowMapper<Userinfo>(Userinfo.class),3);
Userinfo userinfo = jdbcTemplate.query(
"select * from userinfo where id=?",
new ResultSetExtractor<Userinfo>() {
@Override
public Userinfo extractData(ResultSet rs) throws
SQLException, DataAccessException {
Userinfo userinfo = null;
if(rs.next()){
int id = rs.getInt(1);
byte[] image = lobHandler.getBlobAsBytes(rs, 2);
String description =
lobHandler.getClobAsString(rs,3);
userinfo = new Userinfo();
userinfo.setId(id);
userinfo.setImages(image);
userinfo.setDescription(description);
}
return userinfo;
}
},3);
System.out.println(userinfo);
}
}

2、NamedParameterJdbcTemplate

2.1、概述

2.1.1、基本介紹

在經典的 JDBC 用法中, SQL 引數是用佔位符 ? 表示,並且受到位置的限制. 定位引數的問題在於, 一旦引數的順序發生變化, 就必須改變引數繫結.在 Spring JDBC 框架中, 繫結 SQL 引數的另一種選擇 是使用具名引數(named parameter).

那麼什麼是具名引數? 具名引數: SQL 按名稱(以冒號開頭)而不是按位置進行指定. 具名引數更易於維護, 也提升了可讀性. 具名 引數由框架類在執行時用佔位符取代 具名引數只在 NamedParameterJdbcTemplate 中得到支援。NamedParameterJdbcTemplate可以使 用全部jdbcTemplate方法。

2.1.2、原始碼

/**
* 通過觀察原始碼我們發現,NamedParameterJdbcTemplate裡面封裝了一個JdbcTemplate物件
* 只不過把它看成了介面型別JdbcOperations。
*/
public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
{
/** The JdbcTemplate we are wrapping. */
private final JdbcOperations classicJdbcTemplate;

public NamedParameterJdbcTemplate(DataSource dataSource) {
Assert.notNull(dataSource, "DataSource must not be null");
this.classicJdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* 使用JdbcOperations 構建一個NamedParameterJdbcTemplate
*/
public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate) {
Assert.notNull(classicJdbcTemplate, "JdbcTemplate must not be null");
this.classicJdbcTemplate = classicJdbcTemplate;
}
//其餘程式碼略
}

2.2、入門案例

2.2.1、匯入座標

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

2.2.2、編寫實體類

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class Account implements Serializable{
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}

2.2.3、編寫配置類

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
public DataSource createDataSource(){
DriverManagerDataSource dataSource = new
DriverManagerDataSource(url,username,password);
dataSource.setDriverClassName(driver);
return dataSource;
}
@Bean
public NamedParameterJdbcTemplate createNamedJdbcTemplate(JdbcTemplate
jdbcTemplate){
return new NamedParameterJdbcTemplate(jdbcTemplate);
}
}

2.2.4、編寫配置檔案

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
jdbc.username=root
jdbc.password=1234

2.2.5、測試方法

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testFindMore(){
// List<Account> accounts = jdbcTemplate.query("select * from account
where id in (?,?)",new Object[]{1,2},new BeanPropertyRowMapper<Account>
(Account.class));
Map<String,List<Integer>> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
account where id in (:ids)",map,new BeanPropertyRowMapper<Account>
(Account.class));
System.out.println(accounts);
}
@Test
public void testNamedParameter(){
Account account = new Account();
account.setName("test");
account.setMoney(12345d);
BeanMap beanMap = BeanMap.create(account);
namedParameterJdbcTemplate.update("insert into
account(name,money)values(:name,:money)",beanMap);
}
}

3、JdbcTemplate的實現原理

3.1、自定義JdbcTemplate

3.1.1、建立工程並匯入座標

<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
</dependencies>

3.1.2、編寫自定義JdbcTemplate

/**
* 自定義JdbcTemplate
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class JdbcTemplate {
//定義資料來源
private DataSource dataSource;
//通過建構函式給資料來源賦值
public JdbcTemplate(DataSource dataSource){
this.dataSource = dataSource;
}
//通過set方法給資料來源賦值
}
/**
* 查詢方法
* @param sql sql語句
* @param rsh 結果集處理器
* @param params sql語句的引數
* @return
*/
public Object query(String sql, ResultSetHandler rsh , Object... params){
//1.判斷是否有資料來源,沒有資料來源就直接拋異常
if(dataSource == null){
throw new NullPointerException("DataSource can not empty!");
}
//2.定義連線和處理物件
Connection connection = null;
PreparedStatement pstm = null;
ResultSet rs = null;
try {
//2.獲取連線
connection = dataSource.getConnection();
//3.獲取預處理物件
pstm = connection.prepareStatement(sql);
//4.獲取引數元資訊
ParameterMetaData pmd = pstm.getParameterMetaData();
//5.獲取引數個數
int parameterCount = pmd.getParameterCount();
//6.驗證引數
if(parameterCount > 0){
if(params == null ){
throw new NullPointerException("Parameter can not be null
!");
}
if(parameterCount != params.length){
throw new IllegalArgumentException("Incorrect parameter
count: expected "+String.valueOf(parameterCount)+", actual
"+String.valueOf(params.length));
}
//7.給引數賦值
for(int i=0;i<parameterCount;i++){
pstm.setObject((i+1),params[i]);
}
}
//8.驗證通過,執行SQL語句
rs = pstm.executeQuery();
//9.處理結果集:策略模式
return rsh.handle(rs);
}catch (Exception e){
throw new RuntimeException(e);
}finally {
release(connection,pstm,rs);
}
}
/**
* 增刪改操作
* @return
*/
public int update(String sql,Object... params){
//1.判斷是否有資料來源,沒有資料來源就直接拋異常
if(dataSource == null){
throw new NullPointerException("DataSource can not empty!");
}
//2.定義連線和處理物件
Connection connection = null;
PreparedStatement pstm = null;
try {
//2.獲取連線
connection = dataSource.getConnection();
//3.獲取預處理物件
pstm = connection.prepareStatement(sql);
//4.獲取引數元資訊
ParameterMetaData pmd = pstm.getParameterMetaData();
//5.獲取引數個數
int parameterCount = pmd.getParameterCount();
//6.驗證引數
if(parameterCount > 0){
if(params == null ){
throw new NullPointerException("Parameter can not be null
!");
}
if(parameterCount != params.length){
throw new IllegalArgumentException("Incorrect parameter
count: expected "+String.valueOf(parameterCount)+", actual
"+String.valueOf(params.length));
}
//7.給引數賦值
for(int i=0;i<parameterCount;i++){
pstm.setObject((i+1),params[i]);
}
}
//8.驗證通過,執行SQL語句
return pstm.executeUpdate();
}catch (Exception e){
throw new RuntimeException(e);
}finally {
release(connection,pstm,null);
}
}
private void release(Connection conn, PreparedStatement pstm, ResultSet rs){
if(rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(pstm != null){
try {
pstm.close();
}catch (Exception e){
}
if(conn != null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}

3.2、自定義RowMapper

3.2.1、定義介面

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public interface ResultSetHandler<T> {
/**
* 處理結果集
* @param rs
* @return
*/
Object handle(ResultSet rs)throws Exception;
}

3.2.2、提供不同實現

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class BeanHandler<T> implements ResultSetHandler {
private Class<T> requiredType;
private BeanListHandler<T> beanListHandler;
/**
* 覆蓋預設無參構造
* @param requriedType
*/
public BeanHandler(Class requriedType){
this.requiredType = requriedType;
}
public BeanHandler(BeanListHandler beanListHandler) {
this.beanListHandler = beanListHandler;
}
if(beanListHandler != null){
return beanListHandler.handle(rs).get(0);
}
//1.定義返回值
T bean = null;
//2.由於是查詢一個,所以只需判斷rs能往下走,不用while迴圈即可
if(rs.next()){
//3.例項化bean物件
bean = requiredType.newInstance();
//4.獲取引數元資訊
ResultSetMetaData rsmd = rs.getMetaData();
//5.取出引數個數
int columnCount = rsmd.getColumnCount();
//6.遍歷引數個數
for(int i=0;i<columnCount;i++){
//7.取出列名稱
String columnLabel = rsmd.getColumnLabel(i+1);
//8.取出列的值
Object value = rs.getObject(columnLabel);
//9.建立實體類的屬性描述器,使用內省填充物件資料
PropertyDescriptor pd = new
PropertyDescriptor(columnLabel,requiredType);
//10.獲取屬性的寫方法
Method method = pd.getWriteMethod();
//11.填充資料
method.invoke(bean,value);
}
}
//返回
return bean;
}
}

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class BeanListHandler<T> implements ResultSetHandler {
private Class<T> requiredType;
/**
* 覆蓋預設無參構造
* @param requriedType
*/
public BeanListHandler(Class requriedType){
this.requiredType = requriedType;
}
@Override
public List<T> handle(ResultSet rs) throws Exception{
//1.定義返回值
List<T> list = new ArrayList();
T bean = null;
//3.例項化bean物件
bean = requiredType.newInstance();
//4.獲取引數元資訊
ResultSetMetaData rsmd = rs.getMetaData();
//5.取出引數個數
int columnCount = rsmd.getColumnCount();
//6.遍歷引數個數
for(int i=0;i<columnCount;i++){
//7.取出列名稱
String columnLabel = rsmd.getColumnLabel(i+1);
//8.取出列的值
Object value = rs.getObject(columnLabel);
//9.建立實體類的屬性描述器,使用內省填充物件資料
PropertyDescriptor pd = new
PropertyDescriptor(columnLabel,requiredType);
//10.獲取屬性的寫方法
Method method = pd.getWriteMethod();
//11.填充資料
method.invoke(bean,value);
}
//12.給list填充資料
list.add(bean);
}
//返回
return list;
}
}

3.3、測試

3.3.1、建立測試工程並匯入座標

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>tx02_designjdbctemplate</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

3.3.2、編寫實體類

/**
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public class Account implements Serializable{
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}

3.3.3、編寫配置類

3.3.4、編寫配置檔案

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
jdbc.username=root
jdbc.password=1234

3.3.5、編寫測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testSave(){
jdbcTemplate.update("insert into
account(name,money)values(?,?)","ccc",1234f);
}
@Test
public void testUpdate(){
jdbcTemplate.update("update account set name=?,money=? where
id=?","ccc",2345f,1);
}
@Test
public void testDelete(){
jdbcTemplate.update("delete from account where id = ?",1);
}
@Test
public void testFindOne(){
Account account = (Account)jdbcTemplate.query("select * from account
where id = ?",new BeanHandler(Account.class),1);
System.out.println(account);
}
@Test
public void testFindAll(){
List<Account> accounts = (List<Account>)jdbcTemplate.query("select *
from account ",new BeanListHandler<Account>(Account.class));
for(Account account : accounts){
System.out.println(account);
}
}
}

3.4、策略模式

二、Spring中的事務

1、API介紹

1.1、PlatformTransactionManager和它的實現類

1.1.1、作用

此介面是Spring的事務管理器核心介面。Spring本身並不支援事務實現,只是負責提供標準,應用底 層支援什麼樣的事務,需要提供具體實現類。此處也是策略模式的具體應用。在Spring框架中,也為我們內 置了一些具體策略,例如:DataSourceTransactionManager,HibernateTransactionManager, JpaTransactionManager,JtaTransactionManager等等。(JpaTransactionManager和 HibernateTransactionManager事務管理器在spring-orm包中)

1.1.2、類檢視

1.1.3、方法說明

public interface PlatformTransactionManager {
/**
* 獲取事務狀態資訊
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
/**
* 提交事務
*/
void commit(TransactionStatus status) throws TransactionException;


void rollback(TransactionStatus status) throws TransactionException;
}

1.2、TransactionDefinition

1.2.1、作用

此介面是Spring中事務可控屬性的頂層介面,裡面定義了事務的一些屬性以及獲取屬性的方法。例如: 事務的傳播行為,事務的隔離級別,事務的只讀,事務的超時等等。通常情況下,我們在開發中都可以配置這 些屬性,以求達到最佳效果。配置的方式支援xml和註解。

1.2.2、類檢視

1.2.3、定義資訊說明

public interface TransactionDefinition {
/**
* REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。
一般的選擇(預設值)
*/
int PROPAGATION_REQUIRED = 0;
/**
* SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行(沒有事務)
*/
int PROPAGATION_SUPPORTS = 1;
/**
* MANDATORY:使用當前的事務,如果當前沒有事務,就丟擲異常
*/
int PROPAGATION_MANDATORY = 2;
/**
* REQUERS_NEW:新建事務,如果當前在事務中,把當前事務掛起。
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
/**
* NEVER:以非事務方式執行,如果當前存在事務,丟擲異常
*/
int PROPAGATION_NEVER = 5;
/**
* NESTED:如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行REQUIRED類似的
操作。
*/
int PROPAGATION_NESTED = 6;
/**
* 事務的隔離級別預設值,當取值-1時,會採用下面的4個值其中一個。
* (不同資料庫的預設隔離級別不一樣)
*/
int ISOLATION_DEFAULT = -1;
/**
* 事務隔離級別為:讀未提交
* 執行效率最高,但什麼錯誤情況也無法避免
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* 事務隔離級別為:讀已提交
* 可以防止髒讀的發生,但是無法防住不可重複讀和幻讀的發生
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* 事務隔離級別為:可重複讀
* 可以防止髒讀和不可重複讀的發生,但是無法防住幻讀的發生
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* 事務隔離級別為:序列化
* 此時所有錯誤情況均可防住,但是由於事務變成了獨佔模式(排他模式),因此效率最低
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
/**
* 超時限制。預設值是-1,沒有超時限制。如果有,以秒為單位進行設定。
*/
int TIMEOUT_DEFAULT = -1;
/**
* 獲取事務傳播行為
*/
int getPropagationBehavior();
/**
* 獲取事務隔離級別
/**
* 獲取事務超時時間
*/
int getTimeout();
/**
* 獲取事務是否只讀
*/
boolean isReadOnly();
/**
* 獲取事務名稱
*/
@Nullable
String getName();
}

1.3、TransactionStatus

1.3.1、作用

此介面是事務執行狀態表示的頂層介面,裡面定義著獲取事務執行狀態的一些方法。

1.3.3、方法說明

public interface TransactionStatus extends SavepointManager, Flushable {
/**
* 是否一個新的事務
*/
/**
* 是否包含儲存點
*/
boolean hasSavepoint();
/**
* 設定事務回滾
*/
void setRollbackOnly();
/**
* 是否是隻回滾事務
*/
boolean isRollbackOnly();
/**
* 重新整理事務
*/
@Override
void flush();
/**
* 事務是否已經完成(標識就是提交或者回滾了)
*/
boolean isCompleted();
}

2、入門案例

2.1、前置說明

1、Spring中事務控制的分類:

程式設計式事務:通過編碼的方式,實現事務控制

宣告式事務:通過配置的方式,實現事務控制。

本案例中,採用的是宣告式事務,且註解驅動的方式配置。

2、案例的選擇 本案例採用的是經典轉賬案例測試事務控制

2.2、環境搭建

2.2.1、匯入座標

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

2.2.2、編寫基礎程式碼

/**
* 賬戶的業務層介面
* @author 黑馬程式設計師
* @Company http://www.itheima.com
*/
public interface AccountService {
/**
* 轉賬
* @param sourceName 轉出賬戶名稱
* @param targetName 轉入賬戶名稱
* @param money 轉賬金額
*/
void transfer(String sourceName,String targetName,Double money);
}