前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从Spring data jpa看Mybatis, 实现自己的JpaMapper

从Spring data jpa看Mybatis, 实现自己的JpaMapper

作者头像
品茗IT
发布2023-10-22 15:14:30
4020
发布2023-10-22 15:14:30
举报
文章被收录于专栏:品茗IT品茗IT

从Spring data jpa看Mybatis, 实现自己的JpaMapper

一、Spring data jpa

1.1 Spring data jpa概述

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

Jpa是一个标准,就像JTA、JMS这种一样。Java Persistence API里面定义了一对注解,并没有实现。

JPA的实现框架有:Hibernate EntityManager(RedHat)、TopLink Essentials(Oracle/GlassFish,EJB 3.0中的JPA参考实现),Apache OpenJPA(BEA)、EclipseLink(http://www.eclipse.org/eclipselink/)、JDO等ORM框架。

在Spring家族里,有Spring data jpa(https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference)提供ORM,Spring Data JPA的默认实现是Hibernate,当然也可以是其他的JPA Provider。如Spring Data JPA 1.10支持Querydsl 4、Hibernate 5、OpenJPA 2.4 和 EclipseLink 2.6.1。

1.2 Jpa 与mybatis

所以,JPA跟mybatis没关系。 然而,Spring data jpa的风格却特别优雅,我们可以用mybatis去实现这种风格。

如: Spring对JPA实现的核心的API:

  • Repository: 所有接口的父接口,而且是一个空接口,目的是为了统一所有Repository的类型,让组件扫描的时候能进行识。
  • CrudRepository:是Repository的子接口,提供CRUD的功能。
  • PagingAndSortingRepository:是CrudRepository的子接口,添加分页和排序的功能。
  • JpaRepository:是PagingAndSortingRepository的子接口,增加了一些实用的功能,例如批量操作。

这些,我们完全可以用mybatis去实现。实现无sql的开发过程。

1.3 Spring data jpa实例

代码语言:javascript
复制
@Entity
@Table(name = "user_role")
public class UserRole {
   @Id
   private int id;
   @Column(name = "user_name")
   private String userName;
   private String role;
}
public interface UserRoleDao extends CrudRepository<UserRole, Integer> {
   List<UserRole> findByRole(String role);
   @Query("select u from UserRole u where u.userName like ?1%")
   List<UserRole> findByAndSort(String userName, Sort sort);
}

public interface UserRoleDao extends PagingAndSortingRepository<UserRole, Integer> {
   Page<UserRole> findByUserName(String username, Pageable pageable);
}

二、Mybatis优雅编程

2.1 Mybatis原理

在这里插入图片描述
在这里插入图片描述
  • Configuration: MyBatis 或者 MyBatisPlus全局配置对象。
  • MappedStatement:一个 MappedStatement 对象对应 Mapper 配置文件中的一个。select/update/insert/delete 节点,主要描述的是一条 SQL 语句。
  • SqlMethod : 枚举对象 ,MyBatisPlus支持的 SQL 方法。
  • TableInfo:数据库表反射信息 ,可以获取到数据库表相关的信息。
  • SqlSource: SQL 语句处理对象。
  • MapperBuilderAssistant: 用于缓存、SQL 参数、查询方剂结果集处理等。通过 MapperBuilderAssistant 将每一个 mappedStatement 添加到onfiguration 中的 mappedstatements 中。

2.2 Mybatis代码生成工具概述

Mybatis的代码生成工具有很多: mybatis-generator、 easycode 等

这些工具是读取数据库表结构,然后按照模板生成entity、mapper、xml文件的。

如果不想下载插件,不想配置数据源,我们完全可以用js去自己生成code。 比如我自己做的一个:https://www.pomit.cn/java/project/sqlToMapper.html

生成代码,虽然提高了开发效率,但是不方便维护。 开发原则:约定大于配置,配置大于代码。所以有很多动态生成mybatis sqlsource的工具,如tk-mybatis、mybatis-plus。

2.3 Mybatis增强工具的原理

  • tk-mybatis:框架自定义的MapperFactoryBean重写了checkDaoConfig()方法,完成对所有sql语句的设置
  • mybatis-plus:继承MapperAnnotationBuilder,重写了MappedStatement。

Mybatis的MapperAnnotationBuilder是注解方式sql的处理器,其中parseStatement方法就是对Method上的注解进行解析,生成sqlsource并addMappedStatement,如果实现无sql的方式,只要有自己的parseStatement,并addMappedStatement即可。

那如何实现呢?

2.4 实现Spring data jpa风格的Mybatis

2.4.1 Mybatis的addMappedStatement
代码语言:javascript
复制
public MappedStatement addMappedStatement(
     String id,
     SqlSource sqlSource,
     StatementType statementType,
     SqlCommandType sqlCommandType,
     Integer fetchSize,
     Integer timeout,
     String parameterMap,
     Class<?> parameterType,
     String resultMap,
     Class<?> resultType,
     ResultSetType resultSetType,
     boolean flushCache,
     boolean useCache,
     boolean resultOrdered,
     KeyGenerator keyGenerator,
     String keyProperty,
     String keyColumn,
     String databaseId,
     LanguageDriver lang,
     String resultSets) {

这就是一个MappedStatement所需要的东西,虽然很多,但是不用担心,真正需要的就几个:

  1. SqlSource,就是sql
  2. SqlCommandType定义CRUD类型
  3. resultType返回值类型
  4. keyGenerator主键生成策略

其他的不是很重要。最重要的是SqlSource该怎么写呢?

languageDriver.createSqlSource(configuration, sql, parameterTypeClass);

这里使用configuration、注解的sql和参数一起创建了SqlSource。所以,思路来了,我们可以使用Java Persistence API的注解和反射去拼接SQL!

2.4.2 获取Mapper

同时,我们也可以通过手段获取到Mapper。

代码语言:javascript
复制
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;

for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
     org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
     MapperRegistry mapperRegistry = configuration.getMapperRegistry();
     List<Class<?>> mappers = new ArrayList<>(mapperRegistry.getMappers());
     // 可以记录下所有的mapper
}
// 此后可以扫描mapper的所有方法,可以按照方法是否存在注解去兼容mybatis常规写法和自动生成代码。

在Spring环境下,我们很容易获取到SqlSessionFactory,通过SqlSessionFactory,我们就可以获取到我们想要的东西: 这里,我们可以获取到Configuration和mappper。这两个很有用, 在MapperAnnotationBuilder中,会通过Configuration和mappper生成MapperBuilderAssistant,然后MapperBuilderAssistant调用addMappedStatement添加自定义MappedStatement。

2.4.3 定义实体

为了自动生成SQL,就需要我们根据实体去拼接SQL。那就解决两个问题:生成哪些方法的sql、实体从哪儿来。 当然有办法:

  1. 可以模仿Spring data jpa的CrudRepository和PagingAndSortingRepository,建立一个CrudMapper和PagingAndSortingMapper,加上泛型,泛型来作为实体,可以使用Java Persistence API的注解,如
代码语言:javascript
复制
 import javax.persistence.Table;
 import javax.persistence.Column;
 import javax.persistence.Id;
 @Table(name = "f_auth_apply")
 public class UserAuthApply {
     @Id
     @Column(name = "user_name")
     private String userName;
     @Column(name = "role_name")
     private String roleName;
 }
  1. CrudMapper和PagingAndSortingMapper定义常用的方法,如save、update、delete等,并实现findBy+字段名进行查询、deleteBy + 字段名规则。
  2. 让获取到的Mapper,继承CrudMapper和PagingAndSortingMapper,这样我们就可以通过解析继承的接口,获取到泛型类:
代码语言:javascript
复制
Class<?> interfases[] = mapper.getInterfaces();
if (interfases == null || interfases.length < 1) {
        return NO_MAPPER;
}
for (Class<?> interfase : interfases) {
        if (ReflectUtil.checkTypeFit(interfase, JMapper.class)) {
                if (interfase.equals(SimpleShardingMapper.class)) {
                        return SHARDING_MAPPER;
                } else if (interfase.equals(PagingAndSortingMapper.class)) {
                        return PAGESORT_MAPPER;
                } else {
                        return CRUD_MAPPER;
                }
        }
}
  1. 同时,可以获取到Mapper的方法:
代码语言:javascript
复制
Method[] methods = mapper.getMethods();
for (Method method : methods) {
        if (method.getAnnotations() == null || method.getAnnotations().length < 1) {
                //无注解,代表是需要被生成sql的方法,有注解无视即可,会自己走mybatis的逻辑。
        }
}
  1. 解析出实体,那自然就能拿到实体上的注解。解析出方法,自然可以按照方法的格式去生成sql了。
2.4.4 实例
代码语言:javascript
复制
@Table(name="user_info")
public class UserInfo implements Serializable {
 private static final long serialVersionUID = 1L;

 @Id
 @Column(name="user_name")
 private String userName;

 @Column()
 //@ShardingKey(prefix="_", methodPrecis="getTable", methodRange = "getTables")
 private String mobile;

 @Column()
 private String name;

 @Column()
 private String passwd;

 @Column()
 private String valid;

}
代码语言:javascript
复制
import org.apache.ibatis.annotations.Mapper;
import com.github.ffch.boot.domain.UserInfo;
import com.github.ffch.jpamapper.core.mapper.CrudMapper;

@Mapper
public interface UserInfoDao extends CrudMapper<UserInfo, String> {
   List<UserInfo> findByUserName(String userName);
   int deleteByUserName(String userName);

   @Select({
       "<script>",
           "SELECT ",
           " user_name as userName,",
           "role as role",
           "FROM user_info",
      "</script>"})
   List<UserInfo> selectAll();
}
代码语言:javascript
复制
@Mapper
public interface UserInfoSortDao extends PagingAndSortingMapper<UserInfo, String>{
   Page<UserInfo> pageByPasswd(String passwd, Pageable pageable);
   List<UserInfo> sortByPasswd(String passwd, Sort sort);
}

2.5 可以做什么

这种方式仅仅可以固定的SQL么,非也。

  1. 可以实现findBy+字段名(And/OR)进行查询、deleteBy + 字段名规则等,就是对检测到某正则匹配的方法名,where条件加上相应的字段名。
  2. 可以实现分页排序,对加上Pageable的方法,模拟@Result(property = “userList”, javaType=List.class, many =@Many(select=“selectUsersByGroupId”), column = “group_id”)})这种形式,对ResultType进行解析,隐式创建一个MappedStatement。
  3. 可以实现联表查询,可以对@JoinColumns和@JoinColumn的含义重写,同样类似分页去模拟一个@Many或者@One注解即可。
  4. 可以实现分表查询,例如指定某个字段为分表字段,制造SQL的时候使用<bind name=\”patternTable\“ value=\”@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTable(liveId), 这种形式可以根据方法动态生成表名。

下面是三年前写的一个小工具: https://www.pomit.cn/jpa-mapper/#/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-07-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从Spring data jpa看Mybatis, 实现自己的JpaMapper
  • 一、Spring data jpa
    • 1.1 Spring data jpa概述
      • 1.2 Jpa 与mybatis
        • 1.3 Spring data jpa实例
        • 二、Mybatis优雅编程
          • 2.1 Mybatis原理
            • 2.2 Mybatis代码生成工具概述
              • 2.3 Mybatis增强工具的原理
                • 2.4 实现Spring data jpa风格的Mybatis
                  • 2.4.1 Mybatis的addMappedStatement
                  • 2.4.2 获取Mapper
                  • 2.4.3 定义实体
                  • 2.4.4 实例
                • 2.5 可以做什么
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档