mybatis注解方式--注解扩展和通用SQL解决方案

本人正在构建的一个开源的项目地址:教育之星​​​​​​​

使用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>

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值