本人正在构建的一个开源的项目地址:教育之星
使用mybatis注解的方式,个人认为确实比XML要方便一些,而且十分利于程序的调试和BUG的跟进。但是XML可以通过工具生成大量的基础的代码这件事情确实是一个方便点。这里的就是基于注解的方式,生成部分通用的接口查询。
定义BaseMapper
package com.clark.mapper;
import com.clark.driver.BaseMapperDriver;
import org.apache.ibatis.annotations.*;
/**
* 基础base
* @param <T>
* @param <K>
*/
public interface BaseMapper<T, K> {
/**
* 插入
* @param model
* @return
*/
@Lang(BaseMapperDriver.class)
@Insert({"<script>", "INSERT INTO ${table} ${values}", "</script>"})
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
Long insert(T model);
/**
* 修改
* @param model
* @return
*/
@Lang(BaseMapperDriver.class)
@Update({"<script>", "UPDATE ${table} ${sets} WHERE ${id}=#{id}", "</script>"})
Long updateById(T model);
/**
* 删除
* @param id
* @return
*/
@Lang(BaseMapperDriver.class)
@Delete("DELETE FROM ${table} WHERE ${id}=#{id}")
Long deleteById(@Param("id") K id);
/**
* 根据ID获取
* @param id
* @return
*/
@Lang(BaseMapperDriver.class)
@Select("SELECT * FROM ${table} WHERE ${id}=#{id}")
T getById(@Param("id") K id);
/**
* 判断是否存在
* @param id
* @return
*/
@Lang(BaseMapperDriver.class)
@Select("SELECT COUNT(1) FROM ${table} WHERE ${id}=#{id}")
Boolean existById(@Param("id") K id);
}
BaseMapperDriver
package com.clark.driver;
import com.clark.annotation.*;
import com.google.common.base.CaseFormat;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 定义自定义的语言
* @author 大仙
*/
public class BaseMapperDriver extends XMLLanguageDriver implements LanguageDriver {
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
//获取当前mapper
Collection<Class<?>> mapperClasses = configuration.getMapperRegistry().getMappers();
Class<?> mapperClass = null;
if(!CollectionUtils.isEmpty(mapperClasses)){
for (Class<?> mc : mapperClasses){
mapperClass = mc;
break;
}
}
//处理SQL
if(mapperClass!=null) {
Class<?>[] generics = getMapperGenerics(mapperClass);
Class<?> modelClass = generics[0];
Class<?> idClass = generics[1];
//表名
script = setTable(script, modelClass);
//主键
script = setId(script, modelClass);
//插入
script = setValues(script,modelClass);
//修改
script = setSets(script, modelClass);
//IN语句
script = setIn(script);
//单表查询结果映射,利用别名
script = setResultAlias(script,modelClass);
}
return super.createSqlSource(configuration, script, parameterType);
}
/**
* 获取泛型
* @param mapperClass
* @return
*/
private Class<?>[] getMapperGenerics(Class<?> mapperClass){
Class<?>[] classes = new Class[2];
Type[] types = mapperClass.getGenericInterfaces();
for(Type type:types){
ParameterizedType parameterizedType = (ParameterizedType)type;
Type[] types1 = parameterizedType.getActualTypeArguments();
classes[0] = (Class<?>) types1[0];
classes[1] = (Class<?>) types1[1];
}
return classes;
}
/**
* 设置表名
* @param script
* @param modelClass
* @return
*/
private String setTable(String script, Class<?> modelClass){
final Pattern inPattern = Pattern.compile("\\$\\{table\\}");
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
//如果注解相同
if (modelClass.isAnnotationPresent(Table.class)) {
script = script.replaceAll("\\$\\{table\\}", modelClass.getAnnotation(Table.class).name());
} else {
System.out.println("=====" + modelClass.getSimpleName());
script = script.replaceAll("\\$\\{table\\}", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, modelClass.getSimpleName()));
}
}
return script;
}
/**
* 替换ID
* @param script
* @param modelClass
* @return
*/
private String setId(String script,Class<?> modelClass){
final Pattern inPattern = Pattern.compile("\\$\\{id\\}");
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
boolean exitIdEnum = false;
for (Field field : modelClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Id.class)) {
script = script.replaceAll("\\$\\{id\\}", field.getAnnotation(Id.class).name());
exitIdEnum = true;
break;
}
}
if (!exitIdEnum) {
script = script.replaceAll("\\$\\{id\\}", "id");
}
}
return script;
}
/**
* 替换sets
* @param script
* @param modelClass
* @return
*/
private String setSets(String script,Class<?> modelClass){
final Pattern inPattern = Pattern.compile("\\$\\{sets\\}");
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
StringBuffer ss = new StringBuffer();
ss.append("<set>");
//是否使用父类的属性
if(modelClass.isAnnotationPresent(UserParent.class)){
//获取父类
Class<?> superClass = modelClass.getSuperclass();
for(Field field : superClass.getDeclaredFields()){
//非public和protected的不处理
if(!(Modifier.isPublic(field.getModifiers())||Modifier.isProtected(field.getModifiers()))){
continue;
}
//如果不显示,直接返回
if (field.isAnnotationPresent(Invisiable.class)) {
continue;
}
//如果不显示,直接返回
if (field.isAnnotationPresent(Id.class)) {
continue;
}
//非驼峰命名规则
String temp = "<if test=\"__field != null\">__column=#{__field},</if>";
if(field.isAnnotationPresent(Column.class)){
ss.append(temp.replaceAll("__field", field.getName())
.replaceAll("__column",field.getAnnotation(Column.class).name() ));
continue;
}
//驼峰命名规则
ss.append(temp.replaceAll("__field", field.getName())
.replaceAll("__column", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
}
}
//本身
for (Field field : modelClass.getDeclaredFields()) {
//如果不显示,直接返回
if (field.isAnnotationPresent(Invisiable.class)) {
continue;
}
//如果不显示,直接返回
if (field.isAnnotationPresent(Id.class)) {
continue;
}
//非驼峰命名规则
String temp = "<if test=\"__field != null\">__column=#{__field},</if>";
if(field.isAnnotationPresent(Column.class)){
ss.append(temp.replaceAll("__field", field.getName())
.replaceAll("__column",field.getAnnotation(Column.class).name() ));
continue;
}
//驼峰命名规则
ss.append(temp.replaceAll("__field", field.getName())
.replaceAll("__column", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
}
ss.deleteCharAt(ss.lastIndexOf(","));
ss.append("</set>");
script = matcher.replaceAll(ss.toString());
}
return script;
}
/**
* 设置Value
* @param script
* @param modelClass
* @return
*/
private String setValues(String script,Class<?> modelClass){
final Pattern inPattern = Pattern.compile("\\$\\{values\\}");
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
StringBuffer ss = new StringBuffer();
List<String> columns = new ArrayList<>();
List<String> values = new ArrayList<>();
//是否使用父类的属性
if(modelClass.isAnnotationPresent(UserParent.class)){
//获取父类
Class<?> superClass = modelClass.getSuperclass();
for(Field field : superClass.getDeclaredFields()){
//非public和protected的不处理
if(!(Modifier.isPublic(field.getModifiers())||Modifier.isProtected(field.getModifiers()))){
continue;
}
//如果不显示,直接返回
if (field.isAnnotationPresent(Invisiable.class)) {
continue;
}
//非驼峰命名规则
values.add("#{"+field.getName()+"}");
if(field.isAnnotationPresent(Column.class)){
columns.add(field.getAnnotation(Column.class).name() );
}else {
//驼峰命名规则
columns.add(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()));
}
}
}
//自身
for (Field field : modelClass.getDeclaredFields()) {
//如果不显示,直接返回
if (field.isAnnotationPresent(Invisiable.class)) {
continue;
}
//非驼峰命名规则
values.add("#{"+field.getName()+"}");
if(field.isAnnotationPresent(Column.class)){
columns.add(field.getAnnotation(Column.class).name() );
}else {
//驼峰命名规则
columns.add(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()));
}
}
ss.append("("+ StringUtils.join(columns.toArray(),",") +") VALUES ("+StringUtils.join(values.toArray(),",")+")");
script = matcher.replaceAll(ss.toString());
}
return script;
}
/**
* in语句
* @param script
* @return
*/
private String setIn(String script){
final Pattern inPattern = Pattern.compile("\\$\\{ins\\}");
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
script = matcher.replaceAll("(<foreach collection=\"$1\" item=\"__item\" separator=\",\" >#{__item}</foreach>)");
}
return script;
}
private String setResultAlias(String script,Class<?> modelClass){
return script;
}
}
相关注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
}
/**
* 指定主键
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
String name() default "";
}
/**
* 属性上标志,不被转换为表对应的属性值
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Invisiable {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String name();
}
/**
* 是否使用父类属性
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserParent {
}
使用,新建UserMapper
@Mapper
public interface UserMapper extends BaseMapper<User,Long>{
@Select("select * from user ")
List<User> getAll();
}
新建测试类
package com.clark.test;
import com.clark.Run;
import com.clark.entity.User;
import com.clark.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Run.class)
@EnableAutoConfiguration
public class RunTests {
@Resource
private UserMapper userMapper;
@Test
public void test() throws Exception {
User user = null;
// 新增测试
System.out.println("------------ 新增测试 ------------");
user = new User();
user.setNameBig("conanli");
user.setTelephone(String.valueOf(Math.random()));
System.out.println("insert: " + userMapper.insert(user));
// 更新测试
System.out.println("------------ 更新测试 ------------");
user = new User();
user.setId(1L);
user.setTelephone("111111");
System.out.println("update: " + userMapper.updateById(user));
// 获取测试
System.out.println("------------ 获取测试 ------------");
System.out.println("user: " + userMapper.getById(1L));
// 删除测试
System.out.println("------------ 删除测试 ------------");
System.out.println("delete: " + userMapper.deleteById(1L));
// 存在测试
System.out.println("------------ 存在测试 ------------");
System.out.println("exist: " + userMapper.existById(1L));
System.out.println("all"+userMapper.getAll());
}
}
关于springboot单元测试可以自行百度。
测试的YML文件配置:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:8306/wecode_saas_qc?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
username: XXX
password: XX
#mybatis的配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
最后POM文件:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- mybatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- alibaba的druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>