Spring Boot 16 Spring JDBC和事務

16。1 概述

在這篇文章中,我將指導您使用Spring JDBC和Spring Transaction建立一個Spring Boot專案並使用資料庫(MySQL)。本文中將討論的知識點包括:

建立本節所需要的資料庫和表。

配置Spring Boot使其能夠連線到資料庫。

使用Spring JDBC處理資料庫。

使用Spring Transaction並解釋Spring Transaction的操作原理。

16。2 初始化表及資料

MySQL:—— 建立表create table BANK_ACCOUNT( ID BIGINT not null, FULL_NAME VARCHAR(128) not null, BALANCE DOUBLE not null) ;—— alter table BANK_ACCOUNT add constraint BANK_ACCOUNT_PK primary key (ID); Insert into Bank_Account(ID, Full_Name, Balance) values (1, ‘Tom’, 1000);Insert into Bank_Account(ID, Full_Name, Balance) values (2, ‘Jerry’, 2000);Insert into Bank_Account(ID, Full_Name, Balance) values (3, ‘Donald’, 3000); commit;

16.3 建立Spring Boot專案

在Eclipse上建立一個Spring Boot專案

Name

: SpringBootJDBC

Group

: me。laocat

Artifact

: SpringBootJDBC

Description

: Spring Boot + Spring JDBC + Spring Transaction

Package

: me。laocat。jdbc

Spring Boot 16 Spring JDBC和事務

選擇要使用的技術和庫:

JDBC

MySQL

Web

Thymeleaf

Spring Boot 16 Spring JDBC和事務

16.4 配置pom.xml

pom。xml檔案的完整內容:

<?xml version=“1。0” encoding=“UTF-8”?> 4。0。0 org。springframework。boot spring-boot-starter-parent 2。1。3。RELEASE <!—— lookup parent from repository ——> me。laocat SpringBootJDBC 1。0。0 SpringBootJDBC Spring Boot and BootStrap 1。8 org。springframework。boot spring-boot-starter-jdbc org。springframework。boot spring-boot-starter-thymeleaf org。springframework。boot spring-boot-starter-web mysql mysql-connector-java runtime org。springframework。boot spring-boot-starter-test test org。springframework。boot spring-boot-maven-plugin

16。5 配置資料來源

Spring Boot 16 Spring JDBC和事務

為了讓Spring可以連線到資料庫,您需要在application。properties檔案中配置必要的引數。

# ===============================# DATABASE# ===============================spring。datasource。driver-class-name=com。mysql。cj。jdbc。Driverspring。datasource。url=jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8spring。datasource。username=rootspring。datasource。password=root

注意:預設情況下,Spring Boot將自動配置Spring JDBC,並建立與Spring JDBC相關的Spring bean。Spring Boot的這些自動配置包括:

DataSourceAutoConfiguration

DataSourceTransactionManagerAutoConfiguration

在Spring中,從查詢語句獲得的代表記錄資料的類稱為模型類。BankAccountInfo類是一個模型。

BankAccountInfo.java

package me。laocat。jdbc。model;/** * 銀行賬戶資訊 */public class BankAccountInfo { private Long id; private String fullName; private double balance; public BankAccountInfo(Long id, String fullName, double balance) { super(); this。id = id; this。fullName = fullName; this。balance = balance; } public Long getId() { return id; } public void setId(Long id) { this。id = id; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this。fullName = fullName; } public double getBalance() { return balance; } public void setBalance(double balance) { this。balance = balance; } }

用於在查詢語句中的1列和模型類中的1個欄位之間對映1-1的類稱為對映器類。BankAccountMapper是這樣的類。

Spring Boot 16 Spring JDBC和事務

BankAccountMapper.java

package me。laocat。jdbc。mapper;import java。sql。ResultSet;import java。sql。SQLException;import org。springframework。jdbc。core。RowMapper;import me。laocat。jdbc。model。BankAccountInfo;/** * 銀行賬戶和資料庫對映類 * */public class BankAccountMapper implements RowMapper { public static final String BASE_SQL = “Select ba。Id, ba。Full_Name, ba。Balance From Bank_Account ba ”; @Override public BankAccountInfo mapRow(ResultSet rs, int rowNum) throws SQLException { Long id = rs。getLong(“id”); String fullName = rs。getString(“Full_Name”); double balance = rs。getDouble(“Balance”); return new BankAccountInfo(id, fullName, balance); } }

BankAccountDAO.java

package me。laocat。jdbc。dao;import java。util。List;import javax。sql。DataSource;import org。springframework。beans。factory。annotation。Autowired;import org。springframework。dao。EmptyResultDataAccessException;import org。springframework。jdbc。core。support。JdbcDaoSupport;import org。springframework。stereotype。Repository;import org。springframework。transaction。annotation。Propagation;import org。springframework。transaction。annotation。Transactional;import me。laocat。jdbc。exception。BankTransactionException;import me。laocat。jdbc。mapper。BankAccountMapper;import me。laocat。jdbc。model。BankAccountInfo;/** * 資料持久化層 * */@Repository@Transactionalpublic class BankAccountDAO extends JdbcDaoSupport{ // 構造方法注入資料來源 @Autowired public BankAccountDAO(DataSource dataSource) { this。setDataSource(dataSource); } /** * 查詢所有銀行賬戶資訊 * @return */ public List getBankAccounts(){ String sql = BankAccountMapper。BASE_SQL; Object[] params = new Object[] {}; BankAccountMapper rowMapper = new BankAccountMapper(); return this。getJdbcTemplate()。query(sql, params, rowMapper); } /** * 根據id查詢銀行賬戶資訊 * @param id * @return */ public BankAccountInfo findBankAccountInfo(Long id){ String sql = BankAccountMapper。BASE_SQL + “ where ba。id=?”; Object[] params = new Object[] {}; BankAccountMapper rowMapper = new BankAccountMapper(); try { return this。getJdbcTemplate()。queryForObject(sql, params, rowMapper); }catch(EmptyResultDataAccessException e){ return null; } } /** * 存款 * * MANDATORY: 必須先建立事務 * @param id * @param amount * @throws BankTransactionException */ @Transactional(propagation = Propagation。MANDATORY) public void addAmount(Long id, double amount) throws BankTransactionException { // 根據id查詢 BankAccountInfo accountInfo = this。findBankAccountInfo(id); if (accountInfo == null) { throw new BankTransactionException(“賬戶沒找 到 ” + id); } double newAmount = accountInfo。getBalance() + amount; if (newAmount < 0) { throw new BankTransactionException(id + “的賬戶餘額不足,餘額(” + accountInfo。getBalance() + “)”); } accountInfo。setBalance(newAmount); String sqlUpdate = “Update Bank_Account set Balance = ? where Id = ?”; this。getJdbcTemplate()。update(sqlUpdate, accountInfo。getBalance(), accountInfo。getId()); } /** * 轉賬 * * REQUIRES_NEW: 表示存在事務先掛起,再建立一個新事務 * @param fromAccountId 轉出賬戶 * @param toAccountId 轉入賬戶 * @param amount 轉賬金額 * @throws BankTransactionException */ @Transactional(propagation = Propagation。REQUIRES_NEW, rollbackFor = BankTransactionException。class) public void sendMoney(Long fromAccountId, Long toAccountId, double amount) throws BankTransactionException { // 存入的賬戶 addAmount(toAccountId, amount); // 取出的賬戶 addAmount(fromAccountId, -amount); } }

BankTransactionException.java

package me。laocat。jdbc。exception;/** * 銀行事務異常 * @author Administrator * */public class BankTransactionException extends Exception { private static final long serialVersionUID = -2309082446702696187L; public BankTransactionException(String message) { super(message); } }

SendMoneyForm.java

package me。laocat。jdbc。form;/** * 轉賬DTO * @author Administrator * */public class SendMoneyForm { private Long fromAccountId; private Long toAccountId; private Double amount; public SendMoneyForm() { } public SendMoneyForm(Long fromAccountId, Long toAccountId, Double amount) { this。fromAccountId = fromAccountId; this。toAccountId = toAccountId; this。amount = amount; } public Long getFromAccountId() { return fromAccountId; } public void setFromAccountId(Long fromAccountId) { this。fromAccountId = fromAccountId; } public Long getToAccountId() { return toAccountId; } public void setToAccountId(Long toAccountId) { this。toAccountId = toAccountId; } public Double getAmount() { return amount; } public void setAmount(Double amount) { this。amount = amount; }}

講解Spring Transaction:

在此示例中,我模擬了銀行交易。一個帳戶向B帳戶傳送700元的金額。因此,將在資料庫中建立兩個操作:

向B帳戶新增700 元。

從A賬戶中減去700 元。

如果第一個操作成功(將 700元新增到B賬戶),但是第二個操作由於某種原因失敗,則銀行將遭受損失。因此,它需要管理事務以確保如果操作失敗,則資料將回滾原始狀態(事務之前)。當所有操作成功時,事務被視為成功。

使用@Transactional(rollbackFor = BankTransactionException。class)註釋方法以告知“ Spring Transaction”“讓AOP應用於此方法”。

Spring Transaction將Spring AOP應用於您的方法,就像更改方法的程式碼,在程式碼片段中新增異常並在發生異常時呼叫Rollback進行交易,然後將異常從方法中丟擲。全部與下圖相同:

Spring Boot 16 Spring JDBC和事務

16.6 Controller

BankAccountController.java

/** * 前端控制器層 */@Controllerpublic class BankAccountController { @Autowired private BankAccountDAO bankAccountDAO; /** * 展示所有賬戶 * @param model * @return */ @RequestMapping(value = “/”, method = RequestMethod。GET) public String showBankAccounts(Model model) { List list = bankAccountDAO。getBankAccounts(); model。addAttribute(“accountInfos”, list); return “accountsPage”; } /** * 跳轉到轉賬頁面 * @param model * @return */ @RequestMapping(value = “/sendMoney”, method = RequestMethod。GET) public String viewSendMoneyPage(Model model) { SendMoneyForm form = new SendMoneyForm(1L, 2L, 700d); model。addAttribute(“sendMoneyForm”, form); return “sendMoneyPage”; } /** * 轉賬 * @param model * @param sendMoneyForm * @return */ @RequestMapping(value = “/sendMoney”, method = RequestMethod。POST) public String processSendMoney(Model model, SendMoneyForm sendMoneyForm) { System。out。println(“轉賬金額 ::” + sendMoneyForm。getAmount()); try { bankAccountDAO。sendMoney(sendMoneyForm。getFromAccountId(), // sendMoneyForm。getToAccountId(), // sendMoneyForm。getAmount()); } catch (BankTransactionException e) { model。addAttribute(“errorMessage”, “Error: ” + e。getMessage()); return “/sendMoneyPage”; } return “redirect:/”; }}

16。7 Thymeleaf模版頁面

Spring Boot 16 Spring JDBC和事務

_menu.html

accountsPage.html

<!DOCTYPE HTML>賬戶 <!—— 引入 _menu。html ——>

所有張華

賬戶 名稱 餘額
。。 。。 。。

sendMoneyPage.html

<!DOCTYPE HTML> 賬戶 <!—— 引入 _menu。html ——>

轉賬

  • 1 - Tom
  • 2 - Jerry
  • 3 - Donald
。。
轉出賬戶
轉入賬戶
餘額

16。8 執行應用

Spring Boot 16 Spring JDBC和事務

訪問: http://localhost:8080