mybatis 也想要類似 spring-data-jpa 那樣只需要寫接口就能查詢的功能怎麼辦?
前言
spring-data-jpa 的只寫接口便可以 CRUD 的能力真的是好爽,然而 mybatis 寫 sql 的靈活又讓我欲罷不能。
我總是希望二者能夠調和一下,爲此我曾想過在同一個項目中同時使用這兩個框架,當然跑起來是可行的,但是總會擔心會出現什麼問題,想要徹底整合卻沒那個實力。。。
在我的不懈努力搜索之下,終於讓我找到一個項目,它可以在 spring-data-jpa 中以 freemarker 模板的形式編寫動態 sql 去執行。看起來很完美的樣子,就是還需要去學習 freemarker 而已。
但是,受此啓發,既然他拓展了 spring-data-jpa,我爲什麼不能拓展一下 mybatis 呢?於是我就編寫了一個擴展包:mybatis-auto-mapper.jar
詳細功能介紹請移步我的個人博客(點擊這裏)查看,這裏就不重複寫一遍了,只介紹如何使用了。
下面說一下使用(創建項目就不貼圖了,直接貼關鍵代碼了):
首先是 pom.xml
在項目中添加以下依賴即可,無需新增任何配置(需要自己下載源碼安裝到本地倉庫。。。)
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>mybatis-auto-mapper</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
然後是實體類
package com.kfyty.mybatis.auto.mapper.entity;
import lombok.Data;
import java.util.Date;
/**
* 功能描述: 實體類
*
* @author [email protected]
* @date 2019/11/16 16:41
* @since JDK 1.8
*/
@Data
public class TestUser {
private Integer id;
private String name;
private Integer age;
private Date createTime;
private int sortIndex;
public TestUser() {
}
public TestUser(String name, Integer age) {
this.name = name;
this.age = age;
this.sortIndex = age;
}
}
Mapper 接口
package com.kfyty.mybatis.auto.mapper.mapper;
import com.github.pagehelper.Page;
import com.kfyty.mybatis.auto.mapper.annotation.AutoMapper;
import com.kfyty.mybatis.auto.mapper.entity.TestUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 功能描述: mapper 接口
*
* @author [email protected]
* @date 2019/11/16 16:39:41
* @since JDK 1.8
*/
@Mapper
public interface TestUserMapper {
@AutoMapper
int insert(@Param("user") TestUser entity);
@AutoMapper
int insertAll(@Param("users") List<TestUser> entities);
/**
* 存在 @Pageable 時,以最後兩個整型參數自動分頁
* @param pageNum
* @param pageSize
* @return
*/
@Pageable
@AutoMapper
Page<TestUser> pageByNameNotNull(int pageNum, int pageSize);
/**
* 自行配置 MybatisPageHelper 進行分頁
* @param pageNum
* @param pageSize
* @return
*/
@AutoMapper
List<TestUser> findByAgeBetweenOrderByCreateTimeDesc(@Param("startAge") Integer startAge, @Param("endAge") Integer endAge, @Param("pageNum") int pageNum, @Param("pageSize") int pageSize);
}
需要提一下的是,裏面集成了 mybatis-page-helper 分頁插件(在此表示感謝),可以直接使用,當然如果想自己配置一個 Bean 的話也不會衝突。如果只想進行配置的話,可以添加如下代碼進行配置:
@Bean("pageInterceptorProperties")
public Properties pageProperties() {
Properties properties = new Properties();
properties.setProperty("supportMethodsArguments", "true");
return properties;
}
啓動類
package com.kfyty.mybatis.auto.mapper;
import com.github.pagehelper.PageInfo;
import com.kfyty.mybatis.auto.mapper.entity.TestUser;
import com.kfyty.mybatis.auto.mapper.mapper.TestUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* 功能描述: 啓動類
*
* @author [email protected]
* @date 2019/11/16 16:35
* @since JDK 1.8
*/
@RestController
@SpringBootApplication
public class MybatisAutoMapperDemoApplication {
@Autowired
private TestUserMapper testUserMapper;
@RequestMapping("demo/insert/{name}/{age}")
public int insert(@PathVariable("name") String name, @PathVariable("age") Integer age) {
return testUserMapper.insert(new TestUser(name, age));
}
@RequestMapping("demo/insert/all/{startIndex}/{count}")
public int insertAll(@PathVariable("startIndex") Integer startIndex, @PathVariable("count") int count) {
List<TestUser> list = new ArrayList<>();
for (int i = 0; i < count; i++, startIndex++) {
list.add(new TestUser("test-" + startIndex, startIndex));
}
return testUserMapper.insertAll(list);
}
@RequestMapping("demo/page/{pageNum}/{pageSize}")
public PageInfo page(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize) {
return PageInfo.of(testUserMapper.pageByNameNotNull(pageNum, pageSize));
}
@RequestMapping("demo/find/age-between/{start}/{end}/{pageNum}/{pageSize}")
public List<TestUser> findByAgeBetweenOrderByCreateTimeDesc(@PathVariable("start") int start, @PathVariable("end") int end, @PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize) {
return testUserMapper.findByAgeBetweenOrderByCreateTimeDesc(start, end, pageNum, pageSize);
}
@Bean("pageInterceptorProperties")
public Properties pageProperties() {
Properties properties = new Properties();
properties.setProperty("supportMethodsArguments", "true");
return properties;
}
public static void main(String[] args) {
SpringApplication.run(MybatisAutoMapperDemoApplication.class, args);
}
}
由於這裏只涉及到了單表操作,所以不需要 Mapper.xml 文件
下面就可以啓動測試了:
-- 測試之前
mysql> select * from test_user;
Empty set (0.00 sec)
-- 測試接口: http://localhost:8080/demo/insert/1/1
mysql> select * from test_user;
+----+------+------+---------------------+------------+
| id | name | age | create_time | sort_index |
+----+------+------+---------------------+------------+
| 15 | 1 | 1 | 2019-12-08 14:38:01 | 1 |
+----+------+------+---------------------+------------+
1 row in set (0.00 sec)
-- 測試接口: http://localhost:8080/demo/insert/all/2/10
mysql> select * from test_user;
+----+---------+------+---------------------+------------+
| id | name | age | create_time | sort_index |
+----+---------+------+---------------------+------------+
| 15 | 1 | 1 | 2019-12-08 14:38:01 | 1 |
| 16 | test-2 | 2 | 2019-12-08 14:39:08 | 2 |
| 17 | test-3 | 3 | 2019-12-08 14:39:08 | 3 |
| 18 | test-4 | 4 | 2019-12-08 14:39:08 | 4 |
| 19 | test-5 | 5 | 2019-12-08 14:39:08 | 5 |
| 20 | test-6 | 6 | 2019-12-08 14:39:08 | 6 |
| 21 | test-7 | 7 | 2019-12-08 14:39:08 | 7 |
| 22 | test-8 | 8 | 2019-12-08 14:39:08 | 8 |
| 23 | test-9 | 9 | 2019-12-08 14:39:08 | 9 |
| 24 | test-10 | 10 | 2019-12-08 14:39:08 | 10 |
| 25 | test-11 | 11 | 2019-12-08 14:39:08 | 11 |
+----+---------+------+---------------------+------------+
11 rows in set (0.00 sec)
下面測試分頁接口:
--接口: http://localhost:8080/demo/find/name-not-null/1/3
-- 日誌如下:
2019-12-08 14:42:01.418 DEBUG 6524 --- [nio-8080-exec-2] c.k.m.j.s.m.T.findByNameNotNull_COUNT : ==> Preparing: SELECT count(0) FROM test_user WHERE (name IS NOT NULL)
2019-12-08 14:42:01.418 DEBUG 6524 --- [nio-8080-exec-2] c.k.m.j.s.m.T.findByNameNotNull_COUNT : ==> Parameters:
2019-12-08 14:42:01.419 DEBUG 6524 --- [nio-8080-exec-2] c.k.m.j.s.m.T.findByNameNotNull_COUNT : <== Total: 1
2019-12-08 14:42:01.421 DEBUG 6524 --- [nio-8080-exec-2] c.k.m.j.s.m.T.findByNameNotNull : ==> Preparing: select * from test_user where ( name is not null ) LIMIT ?
2019-12-08 14:42:01.421 DEBUG 6524 --- [nio-8080-exec-2] c.k.m.j.s.m.T.findByNameNotNull : ==> Parameters: 3(Integer)
2019-12-08 14:42:01.426 DEBUG 6524 --- [nio-8080-exec-2] c.k.m.j.s.m.T.findByNameNotNull : <== Total: 3
--接口: http://localhost:8080/demo/find/age-between/5/10/1/10
-- 日誌如下:
2019-12-08 15:07:41.874 DEBUG 5104 --- [nio-8080-exec-3] dByAgeBetweenOrderByCreateTimeDesc_COUNT : ==> Preparing: SELECT count(0) FROM test_user WHERE age BETWEEN ? AND ?
2019-12-08 15:07:41.874 DEBUG 5104 --- [nio-8080-exec-3] dByAgeBetweenOrderByCreateTimeDesc_COUNT : ==> Parameters: 5(Integer), 10(Integer)
2019-12-08 15:07:41.876 DEBUG 5104 --- [nio-8080-exec-3] dByAgeBetweenOrderByCreateTimeDesc_COUNT : <== Total: 1
2019-12-08 15:07:41.876 DEBUG 5104 --- [nio-8080-exec-3] .T.findByAgeBetweenOrderByCreateTimeDesc : ==> Preparing: select * from test_user where age between ? and ? order by create_time desc LIMIT ?
2019-12-08 15:07:41.877 DEBUG 5104 --- [nio-8080-exec-3] .T.findByAgeBetweenOrderByCreateTimeDesc : ==> Parameters: 5(Integer), 10(Integer), 10(Integer)
2019-12-08 15:07:41.882 DEBUG 5104 --- [nio-8080-exec-3] .T.findByAgeBetweenOrderByCreateTimeDesc : <== Total: 6
最後,插入單個數據時,如果需要返回主鍵值的話,可以使用 @SelectKey 註解,SelectKey.java 文件如下:
package com.kfyty.mybatis.auto.mapper.annotation;
import com.kfyty.mybatis.auto.mapper.enums.SelectKeyOrder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 功能描述: 用於 mapper 接口或方法,插入數據時生成 <selectKey/> 標籤
* 方法註解優先級高於類註解
* insertAll 方法無效
*
* @author [email protected]
* @date 2019/12/20 19:37
* @since JDK 1.8
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface SelectKey {
/**
* 查詢主鍵值 SQL 語句
* @return 默認值爲 MySQL 查詢自增主鍵
*/
String value() default "select last_insert_id()";
/**
* <selectKey/> 標籤執行順序
* @return 默認值爲 SelectKeyOrder.AFTER
*/
SelectKeyOrder order() default SelectKeyOrder.AFTER;
}
最後放上 AutoMapper.java 文件,大致可以瞭解支持哪些功能:
package com.kfyty.mybatis.auto.mapper.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 功能描述: 用於 mapper 接口或方法,即可自動映射方法
*
* @author [email protected]
* @date 2019/11/6 13:37
* @since JDK 1.8
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AutoMapper {
/**
* 更新時指定主鍵屬性,可用於接口配置
* @return 默認值爲 id
*/
String[] primaryKey() default "";
/**
* 實體類後綴,可用於接口配置
* @return 默認值爲 Pojo
*/
String suffix() default "";
/**
* 實體類/Mapper 接口命名不規範時需指定表名,可用於接口配置
* @return 默認值爲 ""
*/
String table() default "";
/**
* 查詢時添加額外的條件,可用於接口配置
* @return 默認值爲 ""
*/
String where() default "";
/**
* where 條件分隔符,可用於接口配置
* @return 默認值爲 "and"
*/
String separator() default "and";
/**
* 指定需要查詢的列,僅用於方法配置
* @return 默認值爲 "*"
*/
String columns() default "*";
/**
* 符合 find*By** 風格命名時,是否從方法解析需查詢的列,僅用於方法配置
* @return 默認值爲 true
*/
boolean parseColumn() default true;
/**
* 插入/更新對象時,遇到 null 是否轉換爲插入數據庫默認值,僅用於方法配置
* @return 默認值爲 false
*/
boolean useDefault() default false;
/**
* 更新對象時,是否允許更新爲 null 值,僅用於方法配置
* @return 默認值爲 false
*/
boolean allowNull() default false;
/**
* 是否繼承類註解 where 配置,僅用於方法配置
* @return 默認值爲 true
*/
boolean extend() default true;
}
看起來是不是挺像那麼回事的呢?
最後的最後,如果需要查看生成的 mapper 標籤的話,在 application.yml 中添加如下配置即可:
logging:
level:
com.kfyty.mybatis.auto.mapper.handle: debug
感興趣的可以查看 github:https://github.com/kfyty/mybatis-auto-mapper