javascript
SpringBoot学习笔记(9)----SpringBoot中使用关系型数据库以及事务处理
在實際的運用開發中,跟數據庫之間的交互是必不可少的,SpringBoot也提供了兩種跟數據庫交互的方式。
1. 使用JdbcTemplate
在SpringBoot中提供了JdbcTemplate模板類,JdbcTemplate提供的方法進行增刪改查的操作。
首先需要在pom文件中添加依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency>還需要在application.properties中配置數據源。
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_study spring.datasource.username=username spring.datasource.password=username spring.datasource.driver-class-name=com.mysql.jdbc.Driver在項目中頂一個dao,entity,service,controller層,添加一個實體類User和dao中的UserDAO接口,并實現它,具體代碼如下:
User.java
package com.wangx.boot.entity;import java.io.Serializable;public class User implements Serializable { private Integer id; private String name; private Integer age; 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 Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }?使用jdbcTemplate必須實現Serializable接口,否則會出現異常。
UserDAO的實現類UserDAOImpl.java
UserDAOImpl.java
package com.wangx.boot.dao.impl;import com.wangx.boot.dao.UserDAO; import com.wangx.boot.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; @Repository public class UserDAOImpl implements UserDAO { @Autowired private JdbcTemplate jdbcTemplate; @Override public Integer insert(User user) { String sql = "insert into tb_user(name, age) values(?,?)"; return jdbcTemplate.update(sql, user.getName(), user.getAge()); } @Override public List<User> findUserById(Integer id) { String sql = "select * from tb_user where id = ?"; return jdbcTemplate.query(sql,new RowMapper<User>() { @Override public User mapRow(ResultSet resultSet, int i) throws SQLException { User user = new User(); user.setName(resultSet.getString("name")); user.setId(resultSet.getInt("id")); user.setAge(resultSet.getInt("age")); return user; } },id); } }使用@Repository講該類管理到Bean中,使用時直接注入該bean,調用方法即可進行添加和查詢的操作。jdbcTemplate中提供了增刪改查,和帶條件查詢等支持,每種方法的具體使用方式可以自行看源碼或官網demo。
2. 使用JPA
SpringBoot的jpa繼承hibernate和JdbcTemplate對數據庫進行操作。
使用jpa也有一些配置:
可以取自己需要的配置在application.properties中配置。示例時使用了
spring.jpa.hibernate.ddl-auto=update//每次使用都檢查表,沒有表時會新建一張表 spring.jpa.show-sql=true //打印sql語句新建一個Book實體類:
package com.wangx.boot.entity;import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Book { @Id private int id; private String name; private String author; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } @Override public String toString() { return "Book{" + "id=" + id + ", name='" + name + '\'' + ", author='" + author + '\'' + '}'; } }新建一個接口BookDAO繼承JpaRepository<Book, Integer>,這里的第一個泛型為實體類型,第二個為主鍵類型:
package com.wangx.boot.dao.impl;import com.wangx.boot.entity.Book; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; public interface BookDAO extends JpaRepository<Book, Integer> { /** * 根據屬性名生成對應的方法名生成對應查詢條件的方法 * @param name * @return */ Book findByName(String name); /** * 自定義sql語句 * @param name * @return */ @Query(value = "select b from Book b where b.name=?1") List<Book> findByBookName(String name); }
JpaRepository接口中提供很多增刪改查的方法,使用時可以不在BookDAO中定一而直接使用jpa中的方法。
測試該DAO的測試類
package com.wangx.boot;import com.wangx.boot.dao.impl.BookDAO; import com.wangx.boot.entity.Book; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.querydsl.QPageRequest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class SpringBootDemo03ApplicationTests { @Autowired private BookDAO bookDAO; @Test public void insert() { Book book = new Book(); book.setAuthor("辰東"); book.setId(1); book.setName("完美世界"); //測試jpa中默認的save方法,當配置了spring.jpa.hibernate.ddl-auto=update后,第一次使用時,當數據庫中沒有這張表時會自動新建表 System.out.println(bookDAO.save(book)); } @Test public void find() { //測試jpa中定義的根據id查詢的方法 System.out.println(bookDAO.findById(0)); } @Test public void findByName() { //jpa提供了findByXxx根據屬性名查找的方法。自動根據該方法名中的屬性名生成對應條件的sql System.out.println(bookDAO.findByName("遮天")); } @Test public void findByBookName() { //使用@Query注解定一自己的sql語句 System.out.println(bookDAO.findByBookName("遮天")); } @Test public void findByPage() { //使用分頁查詢 Pageable pageable = new QPageRequest(0,10); Page<Book> bookPage = bookDAO.findAll(pageable); List<Book> bookList = bookPage.getContent(); for (Book book : bookList) { System.out.println(book); } } }3. SpringBoot整合mybatis
SpringBoot真的簡化了很大開發量,接下來看看SpringBoot中使用mybaits的示例。
引入mybatis依賴
pom.xml
<dependency><groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency>配置數據源,前面已經介紹就不再重復了,接下來配置mybatis一些文件路徑
# 配置mybatis的映射文件路徑,在我的工程里是classpath下的conf下的所有的.xml文件 mybatis.mapper-locations=classpath:/conf/*.xml # 配置實體類的包路徑 mybatis.type-aliases-package=com.wangx.boot.entity接下來看conf/下的userMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.wangx.boot.dao.UserDAO" > <resultMap id="BaseResultMap" type="com.wangx.boot.entiry.User" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="age" property="age" jdbcType="INTEGER" /> </resultMap> <insert id="insert" parameterType="com.wangx.boot.entiry.User"> insert into tb_user(name,age) values (#{name}, #{age}); </insert> <select id="selectById" resultMap="BaseResultMap" parameterType="INTEGER"> select * from tb_user where id = #{id} </select> <select id="selectAll" resultMap="BaseResultMap"> select * from tb_user </select> </mapper>這里定義了三個語句,對應dao層接口的三個方法,一個新增,兩個查詢,namespace一定要對應dao層中DAO的全名稱。如這里的com.wangx.boot.UserDAO。每個查詢語句對應DAO中相對應的方法,UserDAO如下:
package com.wangx.boot.dao;import com.wangx.boot.entiry.User;import java.util.List; public interface UserDAO { Integer insert(User user); User selectById(Integer id); List<User> selectAll(); }在這里需要在springBoot啟動類中添加@MapperScan("com.wangx.boot.dao")注解,掃描持久層接口路徑。否則會找不到bean,在Service中調用UserDAO
package com.wangx.boot.service.impl;import com.wangx.boot.dao.UserDAO; import com.wangx.boot.entiry.User; import com.wangx.boot.service.UserService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class UserServiceImpl implements UserService { @Resource private UserDAO userDAO; @Override public Integer insert(User user) { return userDAO.insert(user); } @Override public User selectById(Integer id) { return userDAO.selectById(id); } @Override public List<User> selectAll() { return userDAO.selectAll(); } }測試:
package com.wangx.boot;import com.wangx.boot.entiry.User; import com.wangx.boot.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class SpringBootDemo03ApplicationTests { @Autowired private UserService userService; @Test public void contextLoads() { User user = new User(); user.setName("張三"); user.setAge(30); System.out.println(userService.insert(user)); } @Test public void selectById() { User user = userService.selectById(2); System.out.println(user); } @Test public void selectAll() { List<User> users = userService.selectAll(); System.out.println(users); } }一個添加方法和兩個查詢方法均測試通過,修改和刪除感興趣的話可以自行編寫。
4. 事務處理
在SpringBoot中可以使用@Transaction注解對方法或類進行事務處理,作用于方法上的demo。
package com.wangx.boot.service;import com.wangx.boot.dao.UserDAO; import com.wangx.boot.dao.impl.BookDAO; import com.wangx.boot.entity.Book; import com.wangx.boot.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("userAndBookService") public class UserAndBookService { @Autowired private BookDAO bookDAO; @Autowired private UserDAO userDAO; @Transactional public String add () { User user = new User(); user.setName("夏利"); user.setAge(30); userDAO.insert(user); boolean flag = true; if (flag) { throw new RuntimeException(); } Book book = new Book(); book.setId(3); book.setName("圣墟"); book.setAuthor("辰東"); return "success"; } }當第一個添加成功后,認為的拋出異常,查看數據庫會發現兩張表都沒有添加成功。這就是使用@Transactional注解后拋異常時進行了事務回滾。因為Service層是處理整個邏輯的,所以事務的處理一般放在整個service層中,這樣可以保證整個業務邏輯的一致性。
下面來看一下事務的一下概念和@Transaction注解的一些屬性的作用
4.1 數據庫的四個特性
⑴ 原子性(Atomicity)
原子性是指事務包含的所有操作要么全部成功,要么全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
⑵ 一致性(Consistency)
一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處于一致性狀態。
拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
⑶ 隔離性(Isolation)
隔離性是當多個用戶并發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個并發事務之間要相互隔離。
即要達到這么一種效果:對于任意兩個并發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在并發地執行。
關于事務的隔離性數據庫提供了多種隔離級別,稍后會介紹到。
⑷ 持久性(Durability)
持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
4.2 Spring定義了七種傳播行為
1.PROPAGATION_REQUIRED – 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
2.PROPAGATION_SUPPORTS – 支持當前事務,如果當前沒有事務,就以非事務方式執行。
3.PROPAGATION_MANDATORY – 支持當前事務,如果當前沒有事務,就拋出異常。
4.PROPAGATION_REQUIRES_NEW – 新建事務,如果當前存在事務,把當前事務掛起。
5.PROPAGATION_NOT_SUPPORTED – 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
6.PROPAGATION_NEVER – 以非事務方式執行,如果當前存在事務,則拋出異常。
7.PROPAGATION_NESTED – 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。
常用的傳播行為是1和4
4.3 Spring中@Transaction注解的屬性
propagation:事務傳播行為。
isolation:事務隔離級別。
readOnly:事務的讀寫性,boolean型。
timeout:超時時間,int型,以秒為單位。
rollbackFor:一組異常類,遇到時回滾。(rollbackFor={SQLException.class})。
rollbackForCalssName:一組異常類名,遇到回滾,類型為string[]。
noRollbackFor:一組異常類,遇到不回滾。
norollbackForCalssName:一組異常類名,遇到時不回滾
4.4 五大隔離級別
ISOLATION_DEFAULT
這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.
另外四個與JDBC的隔離級別相對應;
ISOLATION_READ_UNCOMMITTED
這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數據。
這種隔離級別會產生臟讀,不可重復讀和幻像讀。
ISOLATION_READ_COMMITTED
保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。
這種事務隔離級別可以避免臟讀出現,但是可能會出現不可重復讀和幻像讀。
ISOLATION_REPEATABLE_READ
這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現幻像讀。
它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重復讀)。
ISOLATION_SERIALIZABLE
這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。
除了防止臟讀,不可重復讀外,還避免了幻像讀。
關鍵詞:
臟讀:指一個事務讀取了一個未提交事務的數據
不可重復讀:在一個事務內讀取表中的某一行數據,多次讀取結果不同.一個事務讀取到了另一個事務提交后的數據.
虛讀(幻讀):在一個事務內讀取了別的事務插入的數據,導致前后讀取不一致(insert)
?原文 SpringBoot學習筆記(9)----SpringBoot中使用關系型數據庫以及事務處理
轉載于:https://www.cnblogs.com/xiaoshen666/p/10844012.html
總結
以上是生活随笔為你收集整理的SpringBoot学习笔记(9)----SpringBoot中使用关系型数据库以及事务处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql gid_mysql主从复制5
- 下一篇: jsb调用java_在JS代码中使用反射