SpringBoot整合系列-整合MyBatis
原創作品,可以轉載,但是請標註出處地址:https://www.cnblogs.com/V1haoge/p/9971036.html
SpringBoot整合Mybatis
步驟
第一步:新增必要的jar包
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency>
第二步:新增必要的配置
application.properties
##配置資料來源
spring.datasource.url = jdbc:h2:mem:dbtest
spring.datasource.username = sa
spring.datasource.password = sa
spring.datasource.driverClassName =org.h2.Driver
第三步:新增配置類
// 該配置類用於配置自動掃描器,用於掃描自定義的mapper介面,MyBatis會針對這些介面生成代理來呼叫對應的XMl中的SQL @Configuration @MapperScan("com.example.springbootdemo.mapper") public class MyBatisConfig { }
這個註解必須手動配置是因為mapper介面的位置完全就是使用者自定義的,自動配置的時候也不可能找到還不存在的位置。
第四步:定義實體型別
@Data @NoArgsConstructor @AllArgsConstructor @ToString @EqualsAndHashCode @Builder @ApiModel("書籍模型") public class Book { @ApiModelProperty(value = "書籍ID", notes = "書籍ID",example = "1") private Integer bookId; @ApiModelProperty(value = "書籍頁數", notes = "書籍頁數",example = "100") private Integer pageNum; @ApiModelProperty(value = "書籍名稱", notes = "書籍名稱",example = "Java程式設計思想") private String bookName; @ApiModelProperty(value = "書籍型別", notes = "書籍型別",hidden = false) private BookType BookType; @ApiModelProperty(value = "書籍簡介") private String bookDesc; @ApiModelProperty(value = "書籍價格") private Double bookPrice; @ApiModelProperty(value = "建立時間",hidden = true) private LocalDateTime createTime; @ApiModelProperty(value = "修改時間",hidden = true) private LocalDateTime modifyTime; }
還有一個列舉型別
public enum BookType {
TECHNOLOGY,//技術
LITERARY,//文學
HISTORY//歷史
;
}
實體類中使用了swagger2和Lombok中的註解,需要新增對應的jar包
第五步:定義mapper介面
public interface BookRepository {
int addBook(Book book);
int updateBook(Book book);
int deleteBook(int id);
Book getBook(int id);
List<Book> getBooks(Book book);
}
第六步:定義mapper配置
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springbootdemo.mapper.BookRepository">
<insert id="addBook" parameterType="Book">
INSERT INTO BOOK(
<if test="pageNum != null">
PAGE_NUM,
</if>
<if test="bookType != null">
BOOK_TYPE,
</if>
<if test="bookName != null">
BOOK_NAME,
</if>
<if test="bookDesc != null">
BOOK_DESC,
</if>
<if test="bookPrice != null">
BOOK_PRICE,
</if>
CREATE_TIME,
MODIFY_TIME)
VALUES (
<if test="pageNum != null">
#{pageNum},
</if>
<if test="bookType != null">
#{bookType},
</if>
<if test="bookName != null">
#{bookName},
</if>
<if test="bookDesc != null">
#{bookDesc},
</if>
<if test="bookPrice != null">
#{bookPrice},
</if>
sysdate,sysdate)
</insert>
<update id="updateBook" parameterType="Book">
UPDATE BOOK SET
<if test="pageNum != null">
PAGE_NUM = #{pageNum},
</if>
<if test="bookType != null">
BOOK_TYPE = #{bookType},
</if>
<if test="bookDesc != null">
BOOK_DESC = #{bookDesc},
</if>
<if test="bookPrice != null">
BOOK_PRICE = #{bookPrice},
</if>
<if test="bookName != null">
BOOK_NAME = #{bookName},
</if>
MODIFY_TIME=sysdate
WHERE 1=1
<if test="bookId != null">
and BOOK_ID = #{bookId}
</if>
</update>
<delete id="deleteBook" parameterType="int">
delete from BOOK where BOOK_id=#{bookId}
</delete>
<select id="getBook" parameterType="int" resultMap="bookResultMap">
select * from BOOK where BOOK_ID=#{bookId}
</select>
<select id="getBooks" resultMap="bookResultMap">
select * from BOOK WHERE 1=1
<if test="bookId != null">
and BOOK_ID = #{bookId}
</if>
<if test="pageNum != null">
and PAGE_NUM = #{pageNum}
</if>
<if test="bookType != null">
and BOOK_TYPE = #{bookType}
</if>
<if test="bookDesc != null">
and BOOK_DESC = #{bookDesc}
</if>
<if test="bookPrice != null">
and BOOK_PRICE = #{bookPrice}
</if>
<if test="bookName != null">
and BOOK_NAME = #{bookName}
</if>
</select>
<resultMap id="bookResultMap" type="Book">
<id column="BOOK_ID" property="bookId"/>
<result column="PAGE_NUM" property="pageNum"/>
<result column="BOOK_NAME" property="bookName"/>
<result column="BOOK_TYPE" property="bookType"/>
<result column="BOOK_DESC" property="bookDesc"/>
<result column="BOOK_PRICE" property="bookPrice"/>
<result column="CREATE_TIME" property="createTime"/>
<result column="MODIFY_TIME" property="modifyTime"/>
</resultMap>
</mapper>
在這個配置檔案中我們使用了MyBatis的動態SQL和引數對映
第七步:再次新增必要的配置
application.properties
#配置Xml配置的位置
mybatis.mapper-locations=classpath*:/mapper/*.xml
#配置實體類型別名
mybatis.type-aliases-package=com.example.springbootdemo.entity
這裡的兩個配置也和之前的掃描器註解一樣,都是自動配置時未知的,需要手動配置,當然可能會存在預設的位置,但是一旦我們自定義了,就必須手動新增配置
第八步:定義service和controller
@Service
@Log4j2
public class BookService {
@Autowired
private BookRepository bookRepository;
public ResponseEntity<Book> addBook(final Book book) {
int num = bookRepository.addBook(book);
return ResponseEntity.ok(book);
}
public ResponseEntity<Integer> updateBook(final Book book){
return ResponseEntity.ok(bookRepository.updateBook(book));
}
public ResponseEntity<Integer> deleteBook(final int bookId){
return ResponseEntity.ok(bookRepository.deleteBook(bookId));
}
public ResponseEntity<Book> getBook(final int bookId) {
Book book = bookRepository.getBook(bookId);
return ResponseEntity.ok(book);
}
public ResponseEntity<List<Book>> getBooks(final Book book){
return ResponseEntity.ok(bookRepository.getBooks(book));
}
}
@RestController
@RequestMapping("/book")
@Api(description = "書籍介面")
@Log4j2
public class BookApi {
@Autowired
private BookService bookService;
@RequestMapping(value = "/addBook", method = RequestMethod.PUT)
@ApiOperation(value = "新增書籍", notes = "新增一本新書籍", httpMethod = "PUT")
public ResponseEntity<Book> addBook(final Book book){
return bookService.addBook(book);
}
@RequestMapping(value = "/updateBook", method = RequestMethod.POST)
@ApiOperation(value = "更新書籍", notes = "根據條件更新書籍資訊", httpMethod = "POST")
public ResponseEntity<Integer> updateBook(final Book book){
return bookService.updateBook(book);
}
@RequestMapping(value = "/deleteBook", method = RequestMethod.DELETE)
@ApiOperation(value = "獲取一本書籍", notes = "根據ID獲取書籍", httpMethod = "DELETE")
public ResponseEntity<Integer> deleteBook(final int bookId){
return bookService.deleteBook(bookId);
}
@RequestMapping(value = "/getBook", method = RequestMethod.GET)
@ApiOperation(value = "獲取一本書籍", notes = "根據ID獲取書籍", httpMethod = "GET")
public ResponseEntity<Book> getBook(final int bookId){
return bookService.getBook(bookId);
}
@RequestMapping(value = "/getBooks", method = RequestMethod.GET)
@ApiOperation(value = "獲取書籍", notes = "根據條件獲取書籍", httpMethod = "GET")
public ResponseEntity<List<Book>> getBooks(final Book book){
return bookService.getBooks(book);
}
}
這裡使用了swagger2的註解
至此設定完畢。
第十步:瀏覽器訪問
http://localhost:8080/swagger-ui.html
通過swagger介面可以看到我們定義的介面。
高階功能
分頁(兩種,簡單分頁RowBounds和攔截器分頁,外掛)
RowBounds分頁
使用RowBounds分頁適用於小資料量的分頁查詢
使用方式是在查詢的Mapper介面上新增RowBounds引數即可,service傳參時需要指定其兩個屬性,當前頁和每頁數
1-定義分頁模型
@Data
@Builder
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class MyPage<T> {
private Integer pageId;//當前頁
private Integer pageNum;//總頁數
private Integer pageSize;//每頁數
private Integer totalNum;//總數目
private List<T> body;//分頁結果
private Integer srartIndex;//開始索引
private boolean isMore;//是否有下一頁
}
2-定義mapper
public interface BookRepository {
// 省略多餘內容
int count(Book book);
List<Book> getBooks(Book book, RowBounds rowBounds);
}
BookRepository.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springbootdemo.mapper.BookRepository">
<!--省略多餘內容-->
<select id="getBooks" resultMap="bookResultMap">
select * from BOOK WHERE 1=1
<if test="bookId != null">
and BOOK_ID = #{bookId}
</if>
<if test="pageNum != null">
and PAGE_NUM = #{pageNum}
</if>
<if test="bookType != null">
and BOOK_TYPE = #{bookType}
</if>
<if test="bookDesc != null">
and BOOK_DESC = #{bookDesc}
</if>
<if test="bookPrice != null">
and BOOK_PRICE = #{bookPrice}
</if>
<if test="bookName != null">
and BOOK_NAME = #{bookName}
</if>
</select>
<select id="count" resultType="int">
select count(1) from BOOK WHERE 1=1
<if test="bookId != null">
and BOOK_ID = #{bookId}
</if>
<if test="pageNum != null">
and PAGE_NUM = #{pageNum}
</if>
<if test="bookType != null">
and BOOK_TYPE = #{bookType}
</if>
<if test="bookDesc != null">
and BOOK_DESC = #{bookDesc}
</if>
<if test="bookPrice != null">
and BOOK_PRICE = #{bookPrice}
</if>
<if test="bookName != null">
and BOOK_NAME = #{bookName}
</if>
</select>
<resultMap id="bookResultMap" type="Book">
<id column="BOOK_ID" property="bookId"/>
<result column="PAGE_NUM" property="pageNum"/>
<result column="BOOK_NAME" property="bookName"/>
<result column="BOOK_TYPE" property="bookType"/>
<result column="BOOK_DESC" property="bookDesc"/>
<result column="BOOK_PRICE" property="bookPrice"/>
<result column="CREATE_TIME" property="createTime"/>
<result column="MODIFY_TIME" property="modifyTime"/>
</resultMap>
</mapper>
3-定義service
@Service
@Log4j2
public class BookService {
@Autowired
private BookRepository bookRepository;
// 省略多餘內容
// 使用RowBounds實現分頁
public ResponseEntity<MyPage<Book>> getBooksByRowBounds(int pageId,int pageSize){
MyPage<Book> myPage = new MyPage<>();
myPage.setPageId(pageId);
myPage.setPageSize(pageSize);
List<Book> books = bookRepository.getBooks(Book.builder().build(), new RowBounds(pageId,pageSize));
int totalNum = bookRepository.count(Book.builder().build());
myPage.setBody(books);
myPage.setTotalNum(totalNum);
return ResponseEntity.ok(myPage);
}
}
4-定義controller
@RestController
@RequestMapping("/book")
@Api(description = "書籍介面")
@Log4j2
public class BookApi {
@Autowired
private BookService bookService;
// 省略多餘內容
@RequestMapping(value = "/getBooksPageByRowBounds", method = RequestMethod.GET)
@ApiOperation(value = "分頁獲取書籍", notes = "通過RowBounds分頁獲取書籍", httpMethod = "GET")
public ResponseEntity<PageInfo<Book>> getBooksPageByRowBounds(final int pageId, final int pageNum){
return bookService.getBooksByRowBounds(pageId, pageNum);
}
}
攔截器分頁
當面對大資料量的分頁時,RowBounds就力不從心的,這時需要我們使用分頁攔截器實現分頁。
這裡其實可以直接使用外掛PageHelper,其就是以攔截器技術實現的分頁查詢外掛。
具體使用方法見SpringBoot整合MyBatis分頁外掛PageHelper
自定義型別轉換器(列舉轉換器)
public class BookTypeEnumHandler extends BaseTypeHandler<BookType> {
/**
* 用於定義設定引數時,該如何把Java型別的引數轉換為對應的資料庫型別
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, BookType parameter, JdbcType jdbcType) throws SQLException {
int j = 0;
for (BookType bookType : BookType.values()){
if(bookType.equals(parameter)){
ps.setString(i, j +"");
return;
}
j++;
}
}
/**
* 用於定義通過欄位名稱獲取欄位資料時,如何把資料庫型別轉換為對應的Java型別
*/
@Override
public BookType getNullableResult(ResultSet rs, String columnName) throws SQLException {
int j = Integer.valueOf(rs.getString(columnName));
if(j >= BookType.values().length) {
return null;
}
int i = 0;
for(BookType bookType:BookType.values()){
if(j == i){
return bookType;
}
i++;
}
return null;
}
/**
* 用於定義通過欄位索引獲取欄位資料時,如何把資料庫型別轉換為對應的Java型別
*/
@Override
public BookType getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
/**
* 用定義呼叫儲存過程後,如何把資料庫型別轉換為對應的Java型別
*/
@Override
public BookType getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
使用@Mapper(不常用,可不看)
注意:使用@Mapper註解的時候是不需要新增xml配置Mapper檔案的,SQL指令碼在介面方法的註解內部定義
第一步:定義實體類
@Data
@Builder
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class Tree {
private Integer treeId;
private String treeName;
private Integer treeAge;
private Double treeHight;
private TreeType treeType;
private TreeState treeState;
private String treeDesc;
}
第二步:定義持久層
@Mapper
public interface TreeRepository {
@Insert("INSERT INTO TREE (TREE_NAME,TREE_AGE,TREE_HIGHT,TREE_TYPE,TREE_STATE,TREE_DESC) VALUES (#{treeName},#{treeAge},#{treeHight},#{treeType},#{treeState},#{treeDesc}) ")
int addTree(Tree tree);
// 此處treeState是一個列舉,此處執行一直報錯
@Update("UPDATE TREE SET TREE_STATE=#{treeState} WHERE TREE_ID=#{treeId}")
int updateState(final int treeId, final TreeState treeState);
@Delete("DELETE FROM TREE WHERE TREE_ID=#{treeId}")
int deleteTree(final int treeId);
@Results({
@Result(id = true, column = "TREE_ID",property = "treeId"),
@Result(column = "TREE_NAME",property = "treeName"),
@Result(column = "TREE_AGE", property = "treeAge"),
@Result(column = "TREE_HIGHT",property = "treeHight"),
@Result(column = "TREE_TYPE",property = "treeType",typeHandler = EnumOrdinalTypeHandler.class),
@Result(column = "TREE_STATE",property = "treeState",typeHandler = EnumOrdinalTypeHandler.class),
@Result(column = "TREE_DESC", property = "treeDesc")
})
@Select("SELECT * FROM TREE WHERE TREE_ID=#{treeId}")
Tree getTree(final int treeId);
@Results({
@Result(id = true, column = "TREE_ID",property = "treeId"),
@Result(column = "TREE_NAME",property = "treeName"),
@Result(column = "TREE_AGE", property = "treeAge"),
@Result(column = "TREE_HIGHT",property = "treeHight"),
@Result(column = "TREE_TYPE",property = "treeType",typeHandler = EnumOrdinalTypeHandler.class),
@Result(column = "TREE_STATE",property = "treeState",typeHandler = EnumOrdinalTypeHandler.class),
@Result(column = "TREE_DESC", property = "treeDesc")
})
@Select("SELECT * FROM TREE")
List<Tree> getTrees(RowBounds rowBounds);
}
注意:重點就在這個介面中,我們新增介面註解@Mapper,表示這是一個持久層Mapper,它的例項化依靠SpringBoot自動配置完成。
在介面方法上直接新增對應的執行註解,在註解中直接定義SQL,這種SQL仍然可以使用表示式#{}來獲取引數的值。
注意@Result註解中定義的兩個關於列舉的型別處理器EnumOrdinalTypeHandler,其實其為MyBatis內部自帶的兩種列舉處理器之一,
用於儲存列舉序號,還有一個EnumTypeHandler用於儲存列舉名稱。
第三步:定義service和controller
@Service
@Log4j2
public class TreeService {
@Autowired
private TreeRepository treeRepository;
public ResponseEntity<Tree> addTree(final Tree tree){
treeRepository.addTree(tree);
return ResponseEntity.ok(tree);
}
public ResponseEntity<Tree> updateTree(final int treeId, final TreeState treeState){
treeRepository.updateState(treeId,treeState);
return ResponseEntity.ok(Tree.builder().treeId(treeId).treeState(treeState).build());
}
public ResponseEntity<Integer> deleteTree(final int treeId){
return ResponseEntity.ok(treeRepository.deleteTree(treeId));
}
public ResponseEntity<Tree> getTree(final int treeId){
return ResponseEntity.ok(treeRepository.getTree(treeId));
}
public ResponseEntity<MyPage<Tree>> getTrees(final int pageId,final int pageSize){
List<Tree> trees = treeRepository.getTrees(new RowBounds(pageId,pageSize));
MyPage<Tree> treeMyPage = new MyPage<>();
treeMyPage.setPageId(pageId);
treeMyPage.setPageSize(pageSize);
treeMyPage.setBody(trees);
return ResponseEntity.ok(treeMyPage);
}
}
@RestController
@RequestMapping("/tree")
@Api(description = "樹木介面")
public class TreeApi {
@Autowired
private TreeService treeService;
@RequestMapping(value = "/addTree",method = RequestMethod.PUT)
@ApiOperation(value = "新增樹木",notes = "新增新樹木",httpMethod = "PUT")
public ResponseEntity<Tree> addTree(final Tree tree){
return treeService.addTree(tree);
}
@RequestMapping(value = "/updateTree",method = RequestMethod.POST)
@ApiOperation(value = "更新狀態",notes = "修改樹木狀態",httpMethod = "POST")
public ResponseEntity<Tree> updateTree(final int treeId,final TreeState treeState){
return treeService.updateTree(treeId,treeState);
}
@ApiOperation(value = "獲取樹木",notes = "根據ID獲取一棵樹",httpMethod = "GET")
@RequestMapping(value = "/getTree",method = RequestMethod.GET)
public ResponseEntity<Tree> getTree(final int treeId){
return treeService.getTree(treeId);
}
}
注意:這個例子中更新狀態的時候還是無法成功,這個狀態是列舉值