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);
}
}
注意:这个例子中更新状态的时候还是无法成功,这个状态是枚举值
SpringBoot整合系列-整合MyBatis的更多相关文章
- SpringBoot整合系列--整合MyBatis-plus
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10125279.html SpringBoot整合MyBatis-plus 步骤 第一步: ...
- SpringBoot整合系列-整合SpringMVC
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9984607.html SpringBoot整合Spring MVC 步骤 第一步:添加必 ...
- SpringBoot整合系列-整合JPA
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9959865.html SpringBoot整合JPA进行数据库开发 步骤 第一步:添加必 ...
- SpringBoot整合系列-整合H2
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9959855.html SpringBoot整合H2内存数据库 一般我们在测试的时候习惯于 ...
- SpringBoot整合系列-整合Swagger2
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9959844.html SpringBoot整合Swagger2 步骤 第一步:添加必要的 ...
- SpringBoot整合系列-PageHelper分页插件
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9971043.html SpringBoot整合MyBatis分页插件PageHelper ...
- SpringBoot系列-整合Mybatis(注解方式)
目录 一.常用注解说明 二.实战 三.测试 四.注意事项 上一篇文章<SpringBoot系列-整合Mybatis(XML配置方式)>介绍了XML配置方式整合的过程,本文介绍下Spring ...
- (七) SpringBoot起飞之路-整合SpringSecurity(Mybatis、JDBC、内存)
兴趣的朋友可以去了解一下前五篇,你的赞就是对我最大的支持,感谢大家! (一) SpringBoot起飞之路-HelloWorld (二) SpringBoot起飞之路-入门原理分析 (三) Sprin ...
- SpringBoot 整合jdbc和mybatis
摘要 该文章主要为记录如何在SpringBoot项目中整合JDBC和MyBatis,在整合中我会使用简单的用法和测试用例,毕竟该文章目的是为了整合,而不是教大家如何去使用.希望大家多多包涵. 通用配置 ...
随机推荐
- Centos安装配置Tomcat,并部署web应用
一.安装JDK并配置环境变量 1.检查和删除系统预装jdk //如果操作系统不是最小安装,会默认安装openjdk#rpm -qa | grep java //删除系统预装jdk,可以一条命令直接删除 ...
- connector for python实验
MySQL 是最流行的关系型数据库管理系统,如果你不熟悉 MySQL,可以阅读 MySQL 教程. 下面为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql-conne ...
- myeclipse集成maven
myeclipse集成maven 打开myeclipse---Window----Preferences---Maven4MyEclipse--Installations----Add,如图所示: 这 ...
- SpringBoot报错:Failed to load ApplicationContext( Failed to bind properties under 'logging.level')
引起条件: SpringBoot2.0下yml文件配置SLF4j日志输出日志级别 logging: level: debug 解决方法: 指定系统包路径 logging: root: debug 指定 ...
- CSS content换行实现字符点点点loading效果
CSS代码如下: dot { display: inline-block; height: 1em; line-height: 1; text-align: left; vertical-align: ...
- Mysql 常见问题
------------------------------------------------ ------------------------------------------------ -- ...
- Linux 系统中的内部与外部命令
linux中的命令大致可分为两类,内部命令和外部命令: 内部命令(builtin command):也称shell内嵌命令 外部命令(external command):存放在一个文件中,使用时需要去 ...
- python错误和异常
一:语法错误syntax errors 熟悉语法! 二:异常 ①打印错误信息时,异常的类型作为异常的内置名显示,并以调用栈的形式显示具体信息 ②常见的异常: ...
- 轻量级C#网络通信组件StriveEngine —— C/S通信开源demo(附源码)
前段时间,有几个研究ESFramework网络通讯框架的朋友对我说,ESFramework有点庞大,对于他们目前的项目来说有点“杀鸡用牛刀”的意思,因为他们的项目不需要文件传送.不需要P2P.不存在好 ...
- [Swift]LeetCode241. 为运算表达式设计优先级 | Different Ways to Add Parentheses
Given a string of numbers and operators, return all possible results from computing all the differen ...