Mybatis - 自定义BaseMapper LanguageDriver(注解方式)_mybatis自定义basemapper-程序员宅基地

技术标签: mybatis  

在使用mybatis的注解的形式的时候我们都希望能封装一些基础的方法。本篇内容就是基于此,本篇内容的源码

源码

如果可以,欢迎点个star

BaseMapper如下:

/**
 * 基础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);
}

为了不影响其他的SQL或者方法,这里自定义mybatis的语言:

BaseMapperDriver

具体实现如下:

/**
 * 定义自定义的语言
 * @author 大仙
 */
public class BaseMapperDriver extends XMLLanguageDriver implements LanguageDriver {

    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
        //获取当前mapper
        Class<?> mapperClass = null;
        if(configuration instanceof MybatisConfig){
            mapperClass = MybatisMapperRegistry.getCurrentMapper();
        }
        if(mapperClass == null){
            throw new RuntimeException("解析SQL出错");
        }
        //处理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;
    }
}

那么其中的注解等内容请自行阅读源码,这里讲解核心的当前mapper获取的方法。我们自定义MapperRegistry,重写他的addMapper的方法,捕获当前mapper,并存储,并在使用完成之后进行丢弃。

/**
 * 自定义mapperRegistry
 * @author 大仙
 */
public class MybatisMapperRegistry extends MapperRegistry {


    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    private Configuration config;

    private static Class<?> currentMapper;

    public MybatisMapperRegistry(Configuration config) {
        super(config);
        this.config = config;
    }

    @Override
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

    @Override
    public <T> boolean hasMapper(Class<T> type) {
        return this.knownMappers.containsKey(type);
    }

    @Override
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                currentMapper = type;
                parser.parse();
                currentMapper=null;
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }

    @Override
    public Collection<Class<?>> getMappers() {
        return Collections.unmodifiableCollection(this.knownMappers.keySet());
    }


    @Override
    public void addMappers(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        Iterator var5 = mapperSet.iterator();

        while(var5.hasNext()) {
            Class<?> mapperClass = (Class)var5.next();
            this.addMapper(mapperClass);
        }

    }

    @Override
    public void addMappers(String packageName) {
        this.addMappers(packageName, Object.class);
    }


    public static Class<?> getCurrentMapper() {
        return currentMapper;
    }
}

讲该类,配置到config类中。

/**
 * 重写mybatis的configureation
 * @author 大仙
 */
public class MybatisConfig extends Configuration {

    protected final MapperRegistry mapperRegistry;

    public MybatisConfig(){
        super();
        this.mapperRegistry =  new MybatisMapperRegistry(this);
        this.mapUnderscoreToCamelCase = true;
    }

    @Override
    public MapperRegistry getMapperRegistry() {
        return this.mapperRegistry;
    }
    @Override
    public void addMappers(String packageName, Class<?> superType) {
        this.mapperRegistry.addMappers(packageName, superType);
    }

    @Override
    public void addMappers(String packageName) {
        this.mapperRegistry.addMappers(packageName);
    }

    @Override
    public <T> void addMapper(Class<T> type) {
        this.mapperRegistry.addMapper(type);
    }

    @Override
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

    @Override
    public boolean hasMapper(Class<?> type) {
        return this.mapperRegistry.hasMapper(type);
    }
}

使自定义的config类生效,我这里用的sharding-jdbc做的读写分离,不使用也是一样的配置,初始化SQLSessionFactory并制定config类即可

    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(masterSlaveDataSource());
        bean.setConfiguration(new MybatisConfig());
        return bean.getObject();
    }

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhuwei_clark/article/details/104637566

智能推荐

如何「优雅」地标数据_AI小男孩的博客-程序员宅基地

最近想做一个识别验证码的程序。目标其实很简单,就是识别出某网站验证码的字母和数字。这种类型的验证码已经被做烂了,相应的破解程序也很多。但我只是想学习消遣一下。

QCustomPlot实现图例置于底部_缘如风的博客-程序员宅基地

简介:QCustomPlot是一个小型的Qt画图表的类。只需要包含.h和.cpp文件即可。支持曲线和柱状图等,具体查看官方文档。http://www.qcustomplot.com/。此例是实现图例位置的置于底部(此例是基于2.0.0版本)。查看相关Layouts帮助说明。默认是按照一行一列的方式添加的item。只要更改item的布局方式即可效果如下实现代码如下void C...

ce教程龙头人修改_龙头游戏修改-程序员宅基地

龙头人修改:龙头人的金币公式是*2+1打开 CE修改器点左上角电脑小图标,找到你要修改的程序打开游戏,比如现有金钱100,就搜201(既金币数*2+1) 点击首次搜索,这样左侧会出现几个地址然后在游戏中购买一个物品,让金币减少,接下来再次搜索现有金币数*2+1,点击再次搜索(next scan)双击那个数据,那个数据就到下面去了,可以通过右键 change value然后进行(你想要..._龙头游戏修改

C语言实验十二 指针(二)_Meteor.792的博客-程序员宅基地

注通过该题,希望学生掌握数组元素与指向数组的指针的不同。a[k]、p[k]、*(p+k)、*(a+k)都表示a[k];5、编写程序实现如下功能通过键盘,用指针输入10个元素的值,再通过指针计算各元素的平均值,输出平均值。/*将数组a的首地址送给指针变量p*/(一)进一步理解指针的概念,掌握其在数组和字符串中的应用。3、以下程序是用指针变量输出a数组中的10个元素。(二)学会使用函数的指针和指向函数的指针变量。4.下面程序的功能是输出a数组中的10个元素。(三)了解指向指针的指针的概念及其使用方法。...

随便推点

微信小程序提交订单支付_输入名字和电话号码提交订单的小程序图片_xlm1998的博客-程序员宅基地

主要是前端支付的几个部分**1.首先我们需要登录 **通过wx.login这个api获取到code值,获取到code值后, 我们请求登录的url并且把获取到的code拼接到url上,后台接收code值,登录成功后,返回openid,然后我们把获取到的openid存取到app.globalData里面2.我们支付点击按钮自己定义了一个方法pay()在方法里获取存在app.globalData里的openid 然后通过支付的接口把openid传到后台 返回参数res 然后我们请求wx.request_输入名字和电话号码提交订单的小程序图片

java jar debug日志_java-调试日志记录在Spring Boot 2.0.3中导致Sta..._一北玄冥的博客-程序员宅基地

我最近将Spring Boot应用程序从1.5.10升级到了2.0.3,现在正面临这个问题:当我将logging.level.root = DEBUG添加到application.properties时,出现以下异常:log4j:WARN No appenders could be found for logger (org.springframework.web.context.support...._org.apache.juli.logging.directjdklog:180

vue中在elementUI中报NavigationDuplicated: Avoided redundant navigation to current location_element-ui.common.js:4656 navigationduplicated: av_火红_的博客-程序员宅基地

// 解决ElementUI导航栏中的vue-router在3.0版本以上重复点菜单报错问题const originalPush = VueRouter.prototype.pushVueRouter.prototype.push = function push (location) { return originalPush.call(this, location).catch(err => err)}..._element-ui.common.js:4656 navigationduplicated: avoided redundant navigation

spark 2.1.1安装(hadoop 2.6.0)_波波happy的博客-程序员宅基地

 spark分布式搭建方式大致分为三种:standalone、yarn、mesos。三种分类的区别这里就不一一介绍了,不明白可自行了解。standalone是官方提供的一种集群方式,企业一般不使用。yarn集群方式在企业中应用是比较广泛的,这里也是介绍yarn的集群安装方式。mesos安装适合于超大型集群。...

2021浙江高考首考成绩查询,浙江学考2021出成绩时间?附浙江学考成绩查询入口网址(1月27日16点)..._余丰慧的博客-程序员宅基地

输入分数,查能上的大学选择科目测一测我能上哪些大学高考成绩分析报告选择科目领取你的专属报告 >选择省份关闭请选择科目确定v>浙江学考,已于1月8日正式结束。对于本次考试,浙江省考生最为关心的就是“浙江2021选考成绩什么时候公布”和“2021浙江学考成绩查询时间”。本期圆梦志愿系小编将为大家带来浙江学考2021出成绩时间以及浙江学考成绩查询入口网址。一、浙江学考2021出成绩时间?据悉..._浙江高一首考成绩哪里查

推荐文章

热门文章

相关标签