Mybatis jpa mini 代码解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:

1.1 SqlSession

Mybatis中3个重要的概念:Configuration(容器),SqlSessionFactory(工厂),SqlSession;

相对于Spring中的applicationContext,BeanFactory,Bean。

不同之处在于SqlSession包含了所有的SQL方法,即这个SqlSession有且只有一个。SqlSession可以执行mybatis中注册的所有方法。官方示例说明

<!-- SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:-->SqlSession session = sqlSessionFactory.openSession();
try {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
  session.close();
}<!-- 映射器实例(Mapper Instances)-->SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
} finally {
  session.close();
}

1.2 Namespace

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--  xml中命名空间与java中Mapper接口一致  --><mapper namespace="org.apache.ibatis.submitted.rounding.Mapper"></mapper>

1.3 ResultType 和 ResultMap

ResultMap是myabtis最重要最强大的元素,是SQL列名columnName与POJO属性名filedName的高级结果映射。对ResultMap不熟悉的朋友可以阅读官方文档了解。

ResultType可以理解为mybatis自动映射生成的简单的ResultMap,当POJO中的filedName与数据库ColumnName不一致时,无法完成映射。

1.4 jdbcType和typeHandler

ResultMap中使用

<resultMap id="userResultMap" type="User">
  <!--  主键   -->
  <id property="id" column="user_id" />
  <!--  当column类型与field类型不一致时,需指定jdbcType或typeHandler,二者选其一即可。   -->
  <!--  简单的类型转换只需指定jdbcType即可,需要逻辑处理的使用typeHandler  -->
  <result property="remark" column="remark" jdbcType="CLOB"/>
  <result property="effective" column="is_effective" typeHandler="BooleanTypeHandler"/></resultMap>

sql中使用

<insert id="insertAuthor">
  insert into Author
    (id, username)
  values    <!--  请注意,当sql中参数可能为null时,需要指定jdbcType,不然会出错  -->
    ('1', #{username,jdbcType=VARCHAR})</insert>

1.5 MappedStatement

Mapper中的方法(方法签名和可执行的sql语句)会被封装为MappedStatement注册到Configuration中。

详见mybatis源码MapperBuilderAssistant.addMappedStatement(args);

二、Mybatis JPA

2.1 需求

1)首先,我们希望能够与spring集成,使用spring的依赖注入。

2)其次,我们希望能够兼容spring-mybatis集成的代码,拒绝污染。

3)解析注册ResultMap和MappedStatement。

2.2 主线

1)参考spring data jpa,使用@RepositoryDefinition注解,标记需要自动生成sql的dao。

我们使用@MapperDefinition和@StatementDefinition注解,标记需要自动生成sql的dao和method。

这个是关键,既保证了不污染原有代码,又可以使用spring-mybatis已经实现的依赖注入。

我们只需要在此基础上,对特定注解标注的mapper类和方法做处理即可。

2)参考spring-mybatis

MapperScannerConfigurer,扫描mapper并注册到mybatis Configuration中,继而生成代理类。

MapperAnnotationBuilder实现java注解生成ResultMap和MappedStatement。

2.3 入口-MapperEnhancerScaner

在spring容器初始化后,对@MapperDefinition标注的mapper类进行扫描。

2.4 解析POJO 生成ResultMap

重点:columnName与fieldName映射,特殊字段的jdbcType和typeHandler。

1)columnName与fieldName映射,使用JPA注解 @Cloumn即可,但是,我们希望能够自动转换驼峰与下划线风格,即对于符合规范命名的,不需要注解,直接映射。参见:PersistentUtil,ColumnNameUtil。

      /**
     * 将驼峰标识转换为下划线
     * 
     * @param text
     * @return camel     */
    public static String camelToUnderline(String text) {        if (text == null || "".equals(text.trim())) {            return "";
        }
        StringBuilder result = new StringBuilder(text.length() + 1);
        result.append(text.substring(0, 1));        for (int i = 1; i < text.length(); i++) {            if (!Character.isLowerCase(text.charAt(i))) {
                result.append('_');
            }
            result.append(text.substring(i, i + 1));
        }        return result.toString().toLowerCase();
    }    /**
     * 将下划线标识转换为驼峰
     * 
     * @param text
     * @return underline     */
    public static String underlineToCamel(String text) {        if (text == null || "".equals(text.trim())) {            return "";
        }        int length = text.length();
        StringBuilder result = new StringBuilder();        for (int i = 0; i < length; i++) {            char c = text.charAt(i);            if (c == '_') {                if (++i < length) {
                    result.append(Character.toUpperCase(text.charAt(i)));
                }
            } else {
                result.append(c);
            }
        }        return result.toString();
    }

2)jdbcType和typeHandler

处理了以下3种类型:POJO中的Enum,Boolean,以及数据库中的CLOB,代码见MybatisColumnMeta。

需要强调说明的是,这里为所有的field都声明了jdbcType,是为了规避sql中参数为null时,产生异常。

 1 /** meta resolver */ 2     private static class ColumnMetaResolver { 3  4         public static String resolveJdbcAlias(Field field) { 5  6             Class<?> fieldType = field.getType(); 7             if (field.getType().isEnum()) { 8                 if (field.isAnnotationPresent(Enumerated.class)) { 9                     // 获取注解对象10                     Enumerated enumerated = field.getAnnotation(Enumerated.class);11                     // 设置了value属性12                     if (enumerated.value() == EnumType.ORDINAL) {13                         return "INTEGER";14                     }15                 }16                 return "VARCHAR";17             }18             if (field.isAnnotationPresent(Lob.class)) {19                 if (String.class.equals(fieldType)) {20                     return "CLOB";21                 }22             }23             if (Integer.class.equals(fieldType)) {24                 return "INTEGER";25             }26             if (Double.class.equals(fieldType)) {27                 return "DOUBLE";28             }29             if (Float.class.equals(fieldType)) {30                 return "FLOAT";31             }32             if (String.class.equals(fieldType)) {33                 return "VARCHAR";34             }35             // date类型需声明36             if (java.util.Date.class.isAssignableFrom(fieldType)) {37                 return "TIMESTAMP";38             }39             return null;40         }41 42         public static JdbcType resolveJdbcType(String alias) {43             if (alias == null) {44                 return null;45             }46             try {47                 return JdbcType.valueOf(alias);48             } catch (IllegalArgumentException e) {49                 throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);50             }51         }52 53         @SuppressWarnings("unchecked")54         public static Class<? extends TypeHandler<?>> resolveTypeHandler(Field field) {55             Class<? extends TypeHandler<?>> typeHandlerClass = null;56             if (field.getType().isEnum()) {57                 typeHandlerClass = (Class<? extends TypeHandler<?>>) EnumTypeHandler.class;58                 if (field.isAnnotationPresent(Enumerated.class)) {59                     // 获取注解对象60                     Enumerated enumerated = field.getAnnotation(Enumerated.class);61                     // 设置了value属性62                     if (enumerated.value() == EnumType.ORDINAL) {63                         typeHandlerClass = (Class<? extends TypeHandler<?>>) EnumOrdinalTypeHandler.class;64                     }65                 }66             }67 68             if (field.getType().equals(Boolean.class)) {69                 typeHandlerClass = (Class<? extends TypeHandler<?>>) BooleanTypeHandler.class;70             }71             return typeHandlerClass;72         }73     }

3)ResultMap注册

见ResultMapAdapter.parseResultMap(args);

2.5 MappedStatement注册

分类处理,select需要用到ResultMap,默认为Pojo.getSimpleName() + "ResultMap";

insert和insertSelective的区别:在于null值的处理,假设column_1在数据库设置了默认值,而参数中的field_1为null值,则insert 在数据库写入null,而insertSelective写入数据库默认值.

需要特别说明的是,动态SQL需要使用"<script></script>"标签包围。

对于各种sql方法的语句生成方法,详见com.mybatis.jpa.statement.builder包下的类。

这里以InsertSelective和select为例

 1 public class InsertSelectiveBuilder implements StatementBuildable { 2  3     @Override 4     public String buildSQL(PersistentMeta persistentMeta, Method method) { 5         // columns 6         StringBuilder columns = new StringBuilder(); 7         columns.append("<trim prefix='(' suffix=')' suffixOverrides=',' > "); 8         // values 9         StringBuilder values = new StringBuilder();10         values.append("<trim prefix='(' suffix=')' suffixOverrides=',' > ");11         for (MybatisColumnMeta columnMeta : persistentMeta.getColumnMetaMap().values()) {12             // columns13             columns.append("<if test='" + columnMeta.getProperty() + "!= null'> ");14             columns.append(columnMeta.getColumnName() + ", ");15             columns.append("</if> ");16             // values17             values.append("<if test='" + columnMeta.getProperty() + "!= null'> ");18             values.append(SqlAssistant.resolveSqlParameter(columnMeta) + ", ");19             values.append("</if> ");20         }21 22         columns.append("</trim> ");23         values.append("</trim> ");24 25         return "<script>" + "INSERT INTO " + persistentMeta.getTableName() + columns.toString() + " VALUES "26                 + values.toString() + "</script>";27     }28 29     @Override30     public void parseStatement(MybatisStatementAdapter adapter, PersistentMeta persistentMeta, Method method) {31         // 方法名32         adapter.setMethodName(method.getName());33         // 参数类型34         adapter.setParameterTypeClass(persistentMeta.getType());35         // sqlScript36         adapter.setSqlScript(buildSQL(persistentMeta, method));37         // 返回值类型38         adapter.setResultType(int.class);39         adapter.setResultMapId(null);40 41         adapter.setSqlCommandType(SqlCommandType.INSERT);42 43         // 主键策略44         adapter.setKeyGenerator(new NoKeyGenerator());45         adapter.setKeyProperty(null);46         adapter.setKeyColumn(null);47 48         adapter.parseStatement();49 50     }51 52 }

 

 1 public class SelectBuilder implements StatementBuildable { 2  3     @Override 4     public String buildSQL(PersistentMeta persistentMeta, Method method) { 5         return "SELECT " + persistentMeta.getColumnNames() + " FROM " + persistentMeta.getTableName() 6                 + SqlAssistant.buildSingleCondition(method, persistentMeta); 7     } 8  9     @Override10     public void parseStatement(MybatisStatementAdapter adapter, PersistentMeta persistentMeta, Method method) {11         // 方法名12         adapter.setMethodName(method.getName());13         // 参数类型14         if (method.getParameterTypes().length > 0) {15             // Mybatis mapper 方法最多支持一个参数,先设置成Object.class,mybatis会在sql中解析16             adapter.setParameterTypeClass(Object.class);17         } else {18             adapter.setParameterTypeClass(void.class);19         }20 21         String orderBy = " ";22 23         if (method.isAnnotationPresent(OrderBy.class)) {24             orderBy = " order by " + method.getAnnotation(OrderBy.class).value();25         }26 27         // sqlScript28         adapter.setSqlScript(buildSQL(persistentMeta, method) + orderBy);29         // 返回值类型30         adapter.setResultType(persistentMeta.getType());31         adapter.setResultMapId(persistentMeta.getType().getSimpleName() + "ResultMap");32 33         adapter.setSqlCommandType(SqlCommandType.SELECT);34 35         // 主键策略36         adapter.setKeyGenerator(new NoKeyGenerator());37         adapter.setKeyProperty(null);38         adapter.setKeyColumn(null);39 40         adapter.parseStatement();41 42     }



















本文转自xsster51CTO博客,原文链接: http://blog.51cto.com/12945177/1950668,如需转载请自行联系原作者



相关文章
|
10天前
|
自然语言处理 搜索推荐 数据安全/隐私保护
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
鸿蒙登录页面设计展示了 HarmonyOS 5.0(Next)的未来美学理念,结合科技与艺术,为用户带来视觉盛宴。该页面使用 ArkTS 开发,支持个性化定制和无缝智能设备连接。代码解析涵盖了声明式 UI、状态管理、事件处理及路由导航等关键概念,帮助开发者快速上手 HarmonyOS 应用开发。通过这段代码,开发者可以了解如何构建交互式界面并实现跨设备协同工作,推动智能生态的发展。
91 10
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
|
4月前
|
XML Java 数据库连接
mybatis plus模板快速生成代码
MybatisX 是一款提升开发效率的 IDEA 插件,尤其适用于处理多表情况。通过 MybatisX-Generator,用户能轻松生成实体类、服务类、Mapper 接口及 XML 文件,显著减少工作量。安装步骤简便:通过 File -&gt; Settings -&gt; Plugins -&gt; Browse Repositories 完成搜索与安装流程。集成数据库后,右键所需操作的数据表,选择 MyBatisX-Generator 进行自动化代码生成。更多细节可参考相关教程。
85 0
|
29天前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
28 1
|
2月前
|
机器学习/深度学习 存储 人工智能
强化学习与深度强化学习:深入解析与代码实现
本书《强化学习与深度强化学习:深入解析与代码实现》系统地介绍了强化学习的基本概念、经典算法及其在深度学习框架下的应用。从强化学习的基础理论出发,逐步深入到Q学习、SARSA等经典算法,再到DQN、Actor-Critic等深度强化学习方法,结合Python代码示例,帮助读者理解并实践这些先进的算法。书中还探讨了强化学习在无人驾驶、游戏AI等领域的应用及面临的挑战,为读者提供了丰富的理论知识和实战经验。
65 5
|
3月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
157 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
2月前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
155 10
|
3月前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
41 6
|
2月前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
3月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
588 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
2月前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
45 1

推荐镜像

更多