1 Star 5 Fork 1

蒋固金 / JDBFly

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

第一部分 简介

JDBFly是一个基于JAVA的持久层开发框架,包含两部分内容:Mybatis增强、数据库版本跟踪。在简化常规开发的同时屏蔽数据库的差异,通过JDBFly使开发者更加关注业务本身,如雄鹰般在天空自由翱翔,从繁琐重复的持久层编码中解放出来。

1.1 特性

  • 侵入小:对Mybatis只做增强,对原有原生代码不会产生影响,仅需调整少量JDBFly配置代码
  • 损耗小:启动即会自动注入内置Mapper,提供基本 CRUD,无额外性能损耗
  • 强大的CRUD操作:内置通用Mapper,仅仅通过少量配置即可实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求
  • 支持多种条件查询: 通过字符串或Lambda表达式,方便的编写各类查询条件,开发者可自由使用
  • 多种主键策略:支持6种主键策略(JDBC自增主键方言SQL自定义SQL动态自定义SQLJAVA代码主键标识),可自由配置
  • 支持JPA方法名称解析:支持JPA形式通过方法名称定义Mapper方法
  • 支持自定义Mapper扩展:提供自定义Mapper入口,快速实现常用方法,并可根据实际业务需要自由组合内置Mapper
  • 动态表名:提供自定义动态表名方法,完美解决多租户分表需求
  • 乐观锁:内置乐观锁实现,提供并发处理的快速实现
  • 自定义XML标签扩展:开发者可自由扩展原生Mybatis标签,定制符合自身业务的标签,内置了常用多数据库适配的函数标签
  • 多数据库适配:支持MySQLMariaDBOracle达梦 等多种数据库,针对特殊数据库提供方言接口可有开发者扩展
  • 多种集成方式:支持原生Java项目、SpringSpringBoot项目集成
  • 数据库版本跟踪:对Flyway进行扩展,自动适配数据库脚本,对数据库版本自动维护升级

1.2 支持数据库

标准功能部分支持所有标准数据库,全功能已经测试通过数据库有:MySqlMariaDB H2OraclePostgreSQL达梦

1.3 实现原理

1.3.1 Mybatis增强

JDBFly提供了一些通用的方法,这些通用方法是以接口的形式提供的。接口和方法都使用了泛型,使用该通用方法的接口需要指定泛型的类型。通过Java反射可以得到接口泛型的类型信息,即对应实体类的信息。在实体类上通过特殊的注解完成实体类与数据库关系的映射。

得到了Mapper方法、实体类映射关系之后,通过生成符合Mybatis规范的XML,最终再交由Mybatis解析处理。默认情况下JDBFly不对Mybatis生成的MappedStatement进行修改,但是这个规则并不是强制的,开发者可以自定义构建器进行特殊的处理。JDBFly修改了MapperAnnotationBuilder默认行为,在Mybatis解析完Mapper接口以及关联的XML文件后,进行后置处理,增加自定义增强Mapper的扩展处理。

为了实现对Mybatis默认标签的扩展,JDBFly重写了EntityResolver,将静态DTD文件转换为Java实体,在运行时动态生成DTD规则,允许开发者扩展自己的节点解析器的时候修改DTD实体相关内容。

1.3.2 数据库版本跟踪

JDBFlyflyway进行了二次封装,扩展了针对不同数据库匹配相应脚本的方法。

1.4 安装

使用JDbFly可以直接下载源代码编译或者下载已经编译的jar文件,如果您是使用maven来构建项目,也可以直接在pom.xml中添加JDBFly的坐标:

Maven central

<!-- http://mvnrepository.com/artifact/com.jianggujin/JDBFly -->
<dependency>
    <groupId>com.jianggujin</groupId>
    <artifactId>JDBFly</artifactId>
    <version>最新版本</version>
</dependency>

最新的版本可以从Maven仓库或者码云获取。

如果使用快照SNAPSHOT版本需要添加仓库,且版本号为快照版本 点击查看最新快照版本号

<repository>
    <id>snapshots</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>

注意:引入 JDBFly 之后请不要再次引入 MyBatisMyBatis-Springmybatis-boot-starter,以避免因版本差异导致的问题。

第二部分 Mybatis增强

2.1 快速开始

2.1.1 JAVA集成

2.1.1.1 从XML中构建 SqlSessionFactory

每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为核心的,JDBFly也同样如此。与直接使用Mybatis的区别在于SqlSessionFactory的实例的构建方式,JDBFly需要将SqlSessionFactoryBuilder替换为JSqlSessionFactoryBuilder

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new JSqlSessionFactoryBuilder().build(inputStream);

XML配置文件中包含了对MyBatis系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager),示例如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <package name="com.jianggujin.dbfly.test.mapper" />
  </mappers>
</configuration>

2.1.1.2不使用XML构建SqlSessionFactory

除了使用XML创建配置之外,JDBFly也可以像Mybatis一样直接从Java代码创建。

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new JConfiguration(environment);
configuration.addMappers("com.jianggujin.dbfly.mapper");
SqlSessionFactory sqlSessionFactory = new JSqlSessionFactoryBuilder().build(configuration);

这里需要注意的是与XML创建配置不同的地方,除了需要将SqlSessionFactoryBuilder替换为JSqlSessionFactoryBuilder之外,还需要将Configuration替换为JConfiguration,实际上通过XML方式最终获得的配置类也是JConfiguration

JSqlSessionFactoryBuilderSqlSessionFactoryBuilder的子类,JConfigurationConfiguration的子类,在JDBFly环境下获得的Configuration都应该是JConfiguration

2.1.2 Spring集成

2.1.2.1 通过XML配置

<bean id="sqlSessionFactory" class="com.jianggujin.dbfly.spring.JSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.jianggujin.dbfly.test.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

上面是默认的配置,在配置sqlSessionFactory时需要将SqlSessionFactoryBean替换为JSqlSessionFactoryBean,其他配置与直接使用Mybatis一样,有些时候需要一些特殊的配置,可能会需要自定义Configuration,这时候,一定要使用JConfiguration替换MybatisConfiguration

<bean id="configuration" class="com.jianggujin.dbfly.mybatis.JConfiguration"/>
<bean id="sqlSessionFactory" class="com.jianggujin.dbfly.spring.JSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configuration" ref="configuration"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.jianggujin.dbfly.test.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

2.1.2.2 通过@JMapperScan注解配置

Mybatis官方提供的注解为MapperScan,在使用注解进行配置的时候,开发者需要使用JDBFly提供的JMapperScan替换官方注解。二者注解属性相同。

@Configuration
@JMapperScan("com.jianggujin.dbfly.test.mapper")
public class AppConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder().addScript("schema.sql").build();
    }

    @Bean
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new JSqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        return sessionFactory.getObject();
    }
}

2.1.3 SpringBoot集成

2.1.3.1 自动装配

JDBFly支持SpringBoot自动装配,为了降低集成与迁移成本,JDBFly的自动装配参数与直接使用mybatis-boot-starter保持一致,只是在其基础上增加了JDBFly特有的配置,如果不需要特殊处理,则配置与mybatis-boot-starter相同。需要注意的是,如果没有结合@JMapperScan注解直接使用自动装配,自动装配会从SpringBoot基础包开始扫描,需要在Mapper接口上增加@Mapper注解,否则MyBatis无法判断扫描哪些接口。

例如在properties配置中:

mybatis.configuration.default-enum-type-handler=org.apache.ibatis.type.EnumOrdinalTypeHandler
mybatis.type-aliases-package=com.jianggujin.dbfly.test.entity
mybatis.dbfly.style=camelhump

mybatis.dbfly开头的是JDBFly特有的配置

2.1.2.2 通过@JMapperScan注解配置

SpringBoot环境中同样可以使用@JMapperScan注解,可以与自动装配配合使用,在不使用该注解的情况下,Mybatis从基础包开始扫描,通过@JMapperScan注解可以限定扫描范围,提升扫描装配效率,也是比较推荐的用法。

2.2 对象关系映射

集成完JDBFly之后,需要做的事情就是将项目中的实体类与数据库中的表进行映射。在JDBFly中提供了实体与表映射的注解,此章节仅介绍常规的通用注解,对于有特殊功能的注解,比如主键策略、动态表名相关注解将在对应章节介绍。

比如在数据库中有人员信息表:

CREATE TABLE user_info (
    id INt AUTO_INCREMENT COMMENT '主键',
    user_no VARCHAR(8) NOT NULL COMMENT '人员编号',
    user_name VARCHAR(8) NOT NULL COMMENT '人员姓名',
    PRIMARY KEY (id)
)

则该表对应的Java实体如下:

Public class UserInfo {
    @JUseGeneratedKeys
    private Integer id;
    private String userNo;
    private String userName;
    // 省略gettre和setter
}

@JNameStyle注解

描述:用于配置Java实体与数据库表、Java实体属性与数据库列之间名称的转换关系。

属性:

属性名 必填 说明
value 枚举类型JStyle,指定转换规则,
可选值如下:
normal:原值
camelhump:驼峰转下划线
uppercase:转换为大写
lowercase:转换为小写
camelhumpAndUppercase:驼峰转下划线大写形式
camelhumpAndLowercase:驼峰转下划线小写形式

示例:

@JNameStyle(JStyle.camelhump)
private String userId; // => user_id

@JTable注解

描述:用于指定Java实体对应的数据库表名称,该注解优先级最高,使用该注解之后名称转换规则动态表名将失效,以value的值为准。

属性:

属性名 必填 说明
value 对应数据库表名称
schema 可选schema

示例:

@JTable("TUC_USER") // TUC_USER
public class User {
    // ...
}

@JColumn注解

描述:用于指定Java实体属性对应的数据库列名称,该注解优先级最高,使用该注解之后名称转换规则与将失效,以value的值为准。

属性:

属性名 必填 说明
value 对应数据库列名称

示例:

@JColumn("user_id")
private String userId; // => user_id

@JExcludeInsert注解

描述:用于在插入操作的时候排除对应属性,即执行通用Mapper插入方法的时候,添加@JExcludeInsert注解的属性将会被忽略。

示例:

@JExcludeInsert
private String userId;

@JExcludeSelect注解

描述:用于在查询操作的时候排除对应属性,即执行通用Mapper查询方法的时候,添加@JExcludeSelect注解的属性将会被忽略。

示例:

@JExcludeSelect
private String userId;

@JExcludeUpdate注解

描述:用于在修改操作的时候排除对应属性,即执行通用Mapper修改方法的时候,添加@JExcludeUpdate注解的属性将会被忽略。

示例:

@JExcludeUpdate
private String userId;

@JTransient注解

描述:用于忽略Java实体属性,通常情况下用于在Java实体中冗余属性,但实际该属性没有对应的列,不需要处理,该注解与直接在属性上添加transient关键字效果是一样的。如果将该注解放在Mapper的方法上面,默认解析器将忽略该方法。

示例:

@JTransient
private String userId;

@JJdbcType注解

描述:用于指定Java实体属性对应的Jdbc类型,当某些情况Java实体属性无法被正确识别转换的时候需要添加该注解。

属性:

属性名 必填 说明
value 枚举类型JdbcType
可选值如下:
ARRAYBITTINYINTSMALLINTINTEGER
BIGINTFLOATREALDOUBLENUMERIC
DECIMALCHARVARCHARLONGVARCHARDATE
TIMETIMESTAMPBINARYVARBINARYLONGVARBINARY
NULLOTHERBLOBCLOBBOOLEAN
CURSORUNDEFINEDNVARCHARNCHARNCLOB
STRUCTJAVA_OBJECTDISTINCTREFDATALINK
ROWIDLONGNVARCHARSQLXMLDATETIMEOFFSET

示例:

@JJdbcType(JdbcType.VARCHAR)
private String userId;// => #{userId, jdbcType=VARCHAR}

@JTypeHandler注解

描述:用于指定Java实体属性对应的TypeHandler,一般用于特殊类型的转换,比如枚举的处理。

属性:

属性名 必填 说明
value 类型处理器的实现类

示例:

@JTypeHandler(EnumOrdinalTypeHandler.class)
private TrueFalse isDeleted;// => #{userId, typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler}

@JUseJavaType注解

描述:用于告知通用Mapper,在处理该Java实体属性的时候是否添加javaType配置

示例:

@JUseJavaType
private String userId; // => #{userId, javaType=java.lang.String}

@JUseNotEmpty注解

描述:用于当字符串类型的Java实体属性作为条件时是否增加!=''的判断。

示例:

@JUseNotEmpty
private String userId; // <if test="userId != null and userId != ''">...</if>

@JOrder注解

描述:用于标注Java实体属性为排序属性,在通用Mapper进行默认的查询操作时会将其作为排序条件拼接在最终的SQL语句中。

属性:

属性名 必填 说明
value 枚举类型JOrderStrategy,指定排序的策略,
默认值为ASC,可选值如下:
ASC:升序
DESC:降序
priority 排序的优先级,默认值为1
值越小,将会优先处理排在排序SQL的前面。

示例:

@JOrder
private String userId; // => user_id asc

@JOverwriteGlobal注解

描述:用于覆盖全局的配置,比如在JDBFly中可以指定排除某些Java实体属性在查询的时候不查询,但是在部分Java实体中存在特例,可以通过在对应实体上添加该注解以覆盖全局的配置。

属性:

属性名 必填 说明
value 指定需要覆盖的配置对应的注解类

示例:

@JOverwriteGlobal(JExcludeUpdate.class)
private String userId;

2.3 内置Mapper

JDBFly对常用的CRUD操作做了通用Mapper的封装,开发者只需要将自己的Mapper继承JDBFly内置的通用Mapper即可拥有相应的操作方法。比如有一个用户信息实体UserInfo,需要对该实体进行增删改查的操作,开发者只需要定义一个Mapper继承JDBFly通用Mapper即可。

public interface UserInfoMapper extends JBaseMapper<UserInfo> {
}

JBaseMapper提供了常用操作的很多方法,有些时候并不需要这么多的操作,针对这种场景,开发者只需要按需选择继承相应的Mapper,同时JDBFly允许开发者自己编写Mapper方法与内置通用Mapper方法在一个Mapper中混合使用,默认情况下,开发者自己的方法优先级高于默认方法。

2.3.1 通用内置Mapper

按照具体的操作分类,内置通用Mapper分类如下:

  • 插入类

    Mapper 说明
    JInsertMapper 保存一个实体,null的属性也会保存,不会使用数据库默认值
    JInsertSelectiveMapper 保存一个实体,null的属性不会保存,会使用数据库默认值
  • 删除类

    Mapper 说明
    JDeleteByConditionMapper 根据Condition条件删除数据
    JDeleteByIdMapper 根据主键字段进行删除,方法参数必须包含完整的主键属性
    JDeleteMapper 根据实体属性作为条件进行删除,查询条件使用等号
  • 修改类

    Mapper 说明
    JUpdateByConditionMapper 根据Condition条件修改
    JUpdateByIdMapper 根据主键更新实体全部字段,null值会被更新
    JUpdateSelectiveByConditionMapper 根据Condition条件修改,仅会修改!=null的数据
    JUpdateSelectiveByIdMapper 根据主键更新属性不为null的值
  • 查询类

    Mapper 说明
    JSelectByConditionMapper 根据Condition条件进行查询
    JSelectOneMapper 根据实体中的属性进行查询,只能有一个返回值,
    有多个结果是抛出异常,查询条件使用等号
    JSelectOneByConditionMapper 根据Condition条件进行查询,只能有一个返回值,
    有多个结果是抛出异常
    JSelectMapper 根据实体中的属性值进行查询,查询条件使用等号
    JSelectCountMapper 根据实体中的属性查询总数,查询条件使用等号
    JSelectCountAllMapper 查询总数
    JSelectCountByConditionMapper 根据Condition条件进行查询总数
    JSelectByIdMapper 根据主键字段进行查询,
    方法参数必须包含完整的主键属性,查询条件使用等号
    JSelectAllMapper 查询全部结果
    JExistsMapper 根据实体中的属性判断数据是否存在,查询条件使用等号
    JExistsByIdMapper 根据主键字段判断数据是否存在,
    方法参数必须包含完整的主键属性,查询条件使用等号
    JExistsByConditionMapper 根据Condition条件判断数据是否存在

上面介绍的Mapper为最小的操作单位,JDBFly按照对应的操作分类提供了汇总的基础MapperJBaseInsertMapperJBaseDeleteMapperJBaseUpdateMapperJBaseSelectMapper,分别对应了插入删除修改查询的操作。在大部分情况下,开发者仅需要继承基础的JBaseMapper,该Mapper包含了所有通用的内置方法。

2.3.2 扩展内置Mapper

扩展内置Mapper无法保证通用性,开发者按需选择。

Mapper 说明
JInsertListMapper 批量保存实体,null的属性也会保存,
不会使用数据库默认值
JDeleteByIdsMapper 根据主键集合删除
JSelectByIdsMapper 根据主键集合查询
JSelectOneOptionalByConditionMapper 根据Condition条件进行查询,只能有一个返回值,
有多个结果是抛出异常,需要依赖mybatis3.5+
JSelectOneOptionalMapper 根据实体中的属性进行查询,只能有一个返回值,
有多个结果是抛出异常,查询条件使用等号,
需要依赖mybatis3.5+
JSelectOptionalByIdMapper 根据主键字段进行查询,
方法参数必须包含完整的主键属性,查询条件使用等号,
需要依赖mybatis3.5+
JSelectPageAllMapper 查询全部结果,支持分页
JSelectPageByConditionMapper 根据Condition条件进行查询,支持分页
JSelectPageMapper 根据实体中的属性值进行查询,查询条件使用等号,支持分页
JBasePageMapper 基础Mapper,包含分页

2.4 方法名称解析

使用内置的通用Mapper,可以简化开发,但是有些时候也会有点麻烦,比如通过固定属性作为条件、查询部分属性等,为了进一步简化对数据库的操作,JDBFly支持像JPA那样通过方法名称定义相关的操作。如果需要开启方法名称解析,只需要让对应的Mapper继承JMethodMapper即可,当然JBaseMapper已经默认开启了方法名称解析。

public interface UserInfoMapper extends JMethodMapper<UserInfo> {
    findUserIdAndUserNameByid(Integer id);
}

2.4.1 命名规则

使用方法名称解析功能,Mapper方法需要按照指定的命名规范才能被正常识别与解析,目前方法名称解析仅支持查询和删除,对应的方法名命名规则如下:

查询[(select|find|read|get|query)[Distinct][Exclude][selectProperties]By][whereProperties][OrderBy(orderProperties)]

删除(delete|remove)By[whereProperties]

各部分说明:

  • ():表示必选项
  • []:表示可选项
  • Distinct:存在该关键词将为查询语句添加DISTINCT
  • Exclude:存在该关键词表示后面的查询属性为需要排除的
  • selectProperties:该表达式表示需要查询的属性,不存在该部分则默认查询全部属性,属性首字母大写且属性与属性之间需要使用And分隔
  • whereProperties:该表达式表示where条件需要使用的属性,分段条件之间通过OrAnd拼接,Or优先级大于And,分段条件格式为:peoperty[Keyword],属性首字母大写,需要注意的是对应方法形参数量必须与拆分后的条件参数数量一致
  • orderProperties:该表达式表示Order by部分需要使用的属性,属性首字母大写,多个排序条件之间使用AscDesc分隔,如果该部分表达式不存在则是使用默认的排序配置

作为条件可用关键词:

关键词 示例 SQL代码段
IsNotNull
NotNull
findByIdIsNotNull
findByIdNotNull
id is not null
IsNull
Null
findByIdIsNull
findByIdNull
id is null
IsNot
Not
NotEquals
Ne
findByIdIsNot
findByIdNot
findByIdNotEquals
findByIdNe
id <> ?1
Is
Equals
Eq
findByIdIs
findByIdEquals
findByIdEq
id = ?1
IsGreaterThan
GreaterThan
Gt
findByIdIsGreaterThan
findByIdGreaterThan
findByIdGt
id > ?1
IsGreaterThanEqual
GreaterThanEqual
Ge
findByIdIsGreaterThanEqual
findByIdGreaterThanEqual
findByIdGe
id >= ?1
IsLessThan
LessThan
Lt
findByIdIsLessThan
findByIdLessThan
findByIdLt
id < ?1
IsLessThanEqual
LessThanEqual
Le
findByIdIsLessThanEqual
findByIdLessThanEqual
findByIdLe
id <= ?1
IsBefore
Before
findByIdIsBefore
findByIdBefore
id < ?1
IsBeforeEqual
BeforeEqual
findByIdIsBeforeEqual
findByIdBeforeEqual
id <= ?1
IsAfter
After
findByIdIsAfter
findByIdAfter
id > ?1
IsAfterEqual
AfterEqual
findByIdIsAfterEqual
findByIdAfterEqual
id >= ?1
IsNotIn
NotIn
findByIdIsNotIn
findByIdNotIn
id not in (?1)
IsIn
In
findByIdIsIn
findByIdIn
id in (?1)
IsBetween
Between
findByIdIsBetween
findByIdBetween
id between ?1 and ?2
IsNotBetween
NotBetween
findByIdIsNotBetween
findByIdNotBetween
id not between ?1 and ?2
IsLike
Like
findByIdIsLike
findByIdLike
id like ?1
IsNotLike
NotLike
findByIdIsNotLike
findByIdNotLike
id not like ?1

2.4.2 使用注解解析

在使用方法名称解析的时候,在某些情况下可能会导致方法名称冗长,可以在方法上面搭配注解来缩短方法名称。存在注解优先级大于方法名称解析结果且不同部分之间互不影响。

public interface UserInfoMapper extends JMethodMapper<UserInfo> {
    @JProperty(exclude = { "id" })
    @JOrderBy(value = { @JOrderProperty(value = "userId", strategy = JOrderStrategy.DESC) })
    List<UserInfo> findAll();
}

@JProperty注解

描述:用于配置选择或排除的列对应的属性,等价于查询的[(select|find|read|get|query)[Distinct][Exclude][selectProperties]By]部分。

属性:

属性名 必填 说明
value 用于指定选择的列对应的属性,
优先级高于exclude,如果与exclude都为空则查询全部属性。
exclude 用于指定排除选择的列对应的属性

示例:

@JProperty({"id", "userName"})
public User findByUserId(String userId);

@JWhere注解

描述:用于配置条件部分的属性以及条件,等价于查询或删除的[whereProperties]部分。

属性:

属性名 必填 说明
value 一组动态条件,属性值为@JCriteria注解

示例:

@JWhere(@JCriteria({ @JCriterion("id"), @JCriterion("usrName") }))
User selectIdAndUserNameBy(Integer id, String userName);

@JCriteria注解

描述:用于配置一个动态条件的分组内容。

属性:

属性名 必填 说明
value 动态条件,属性值为@JCriterion注解
isOr 是否为OR条件

示例:

@JWhere(@JCriteria({ @JCriterion("id"), @JCriterion("usrName") }))
User selectIdAndUserNameBy(Integer id, String userName);

@JCriterion注解

描述:用于配置一个动态条件。

属性:

属性名 必填 说明
value 属性名称
isOr 是否为OR条件
type 枚举类型JType,指定排序的策略,
默认值为EQUALS,可选值如下:
IS_NOT_NULLIS_NULLNOT_EQUALS
EQUALSGREATER_THANGREATER_THAN_EQUAL
LESS_THANLESS_THAN_EQUALBEFORE
BEFORE_EQUALAFTERAFTER_EQUAL
NOT_ININNOT_BETWEEN
BETWEENNOT_LIKELIKE

示例:

@JWhere(@JCriteria({ @JCriterion("id"), @JCriterion("usrName") }))
User selectIdAndUserNameBy(Integer id, String userName);

@JOrderBy注解

描述:用于在查询的时候使用的排序条件,等价于查询的[OrderBy(orderProperties)]部分。

属性:

属性名 必填 说明
value 排序属性,属性值为@JOrderProperty注解

示例:

@JOrderBy(@JCriteria("id"))
User selectIdAndUserNameBy(Integer id, String userName);

@JOrderProperty注解

描述:用于配置排序属性以及排序策略。

属性:

属性名 必填 说明
value 排序属性
strategy 枚举类型JOrderStrategy,指定排序的策略,
默认值为ASC,可选值如下:
ASC:升序
DESC:降序

示例:

@JOrderBy(@JCriteria("id"))
User selectIdAndUserNameBy(Integer id, String userName);

2.5 条件构造器

JDBFly内置的通用Mapper中,以ByCondition结尾的方法允许开发者通过复杂的条件进行操作。

JDBFly默认不支持MBG生成Example,如果需要可由开发者自行扩展

JCondition condition = new JCondition(UserInfo.class);
condition.and().andEqualTo("id", 1);
List<UserInfo> list = mapper.selectByCondition(condition);

2.5.1 动态条件

通过JCondition对象的andor方法可以获得JCriteria对象,利用该对象可以实现复杂的条件的动态组合。对应方法后缀效果如下:

方法后缀 参考SQL代码段
IsNull column is null
IsNotNull column is not null
EqualToEt column = value
NotEqualToNe column <> value
GreaterThanGt column > value
GreaterThanOrEqualToGe column >= value
LessThanLt column < value
LessThanOrEqualToLe column <= value
In column in (value)
NotIn column not in (value)
Between column between value1 and value2
NotBetween column not between value1 and value2
Like column like value
LikeLeft column like '%value'
LikeRight column like 'value%'
NotLike column not like value
NotLikeLeft column not like '%value'
NotLikeRight column not like 'value%'
Condition 手写条件,以实际为准
EqualTo 将不为空的字段参数作为相等查询条件
AllEqualTo 将所有字段参数作为相等查询条件,如果字段为null,则为is null

andor的前缀表示生成的条件使用AND或者OR,方法后缀规则一致,不再重复赘述。除了上述通过andor前缀开头的方法构造动态条件以外,还可以通过更简单的链式调用,方法功能与上述方法后缀一致,仅需要将首字母变为小写,默认情况下链式调用的链接方式与获得条件对象时一致,如果需要更改,仅需要调用and()or()方法切换当前链式调用状态。比如有如下条件:

condition.and().andEqualTo("id", 1).andLikeRight("userName", "jiang");

可以简写为:

condition.and().equalTo("id", 1).likeRight("userName", "jiang");

部分条件方法存在简化同义方法,比如上面的例子可以进一步简化为:

condition.and().eq("id", 1).likeRight("userName", "jiang");

2.5.2 排序

JCondition condition = new JCondition(UserInfo.class);
condition.orderBy("id").asc().orderBy("userId").desc();
mapper.selectByCondition(condition));

排序有两种方式,第一种:使用JCondition对象的orderBy方法,通过链式调用构建排序规则;第二种方式:使用JCondition对象的setOrderBySql方法直接设置排序SQL片段。

2.5.3 去重

JCondition condition = new JCondition(UserInfo.class);
condition.setDistinct(true);
mapper.selectByCondition(condition));

去重需要调用JCondition对象的setDistinct方法并设置为true

2.5.4 排他锁

JCondition condition = new JCondition(UserInfo.class);
condition.setForUpdate(true);
mapper.selectByCondition(condition));

排他锁需要调用JCondition对象的setsetForUpdate方法并设置为true

2.5.5 查询或排除列

JCondition condition = new JCondition(UserInfo.class);
condition.selectProperties("id", "userId");
mapper.selectByCondition(condition));

JCondition中提供查询属性和排除属性的方法,分别为selectPropertiesexcludeProperties。查询属性优先级高于排除属性,如果不设置,则默认查询全部属性。

2.5.6 Lambda形式调用

JLambdaCondition<UserInfo> condition = new JLambdaCondition<>(UserInfo.class);
condition.lambdaAnd().andEqualTo(UserInfo::getId, 1);
mapper.selectByCondition(condition);

JCondition为开发者提供了灵活的设置方式,但是通过字符串形式编写条件,容易出现编写错误、或者实体类升级之后,属性修改不同步出现一些错误,JDBFly同时提供了JLambdaCondition,可以通过lambda表达式定义属性,将错误提升到编译阶段,提前暴露问题,JLambdaCondition同时提供了通过lambda调用以及JCondition的所有能力,使用更为灵活。

2.6 主键策略

JDBFly中提供了6种主键策略,可由开发者自由组合使用。每种主键策略对应一个注解,多个注解联合使用时优先选择优先级别较高的注解配置,忽略优先级别低的注解。

@JUseGeneratedKeys注解

描述:第一优先级主键策略,表示是否使用JDBC方式获取主键,其效果等价于在XML文件中使用useGeneratedKeys="true"的形式,同一个实体类中只允许在一个属性上使用该注解。

示例:

@JUseGeneratedKeys
private Integer id;

@JUseIdentityDialect注解

描述:第二优先级主键策略,根据方言配置的数据库类型取回主键,其效果等价于在XML文件中使用selectKey,同一个实体类中只允许在一个属性上使用该注解。

示例:

以内置的MySQL方言为例,假设有如下实例配置:

Public class UserInfo {
    @JUseIdentityDialect
    private Integer id;
    private String userNo;
    private String userName;
    // 省略gettre和setter
}

则会生成如下XML片段:

<insert>
    <selectKey keyProperty="id" order="AFTER" resultType="int">
        SELECT LAST_INSERT_ID()
    </selectKey>
</insert>

@JUseSql注解

描述:第三优先级主键策略,根据配置的获取主键的SQL与主键生成顺序获取主键,其效果等价于在XML文件中使用selectKey,同一个实体类中只允许在一个属性上使用该注解。

属性:

属性名 必填 说明
value 取主键的SQL
order 枚举类型JKeyOrder,指定主键生成顺序,
默认值为BEFORE,可选值如下:
AFTERinsert后执行SQL
BEFOREinsert前执行SQL

示例:

@JUseSql(value = "SELECT LAST_INSERT_ID()", order = JKeyOrder.AFTER)
private Integer id;

@JUseSqlGenerator注解

描述:第四优先级主键策略,结合SQL生成器JSqlGenerator动态生成获取主键的SQL与主键生成顺序获取主键,其效果等价于在XML文件中使用selectKey,同一个实体类中只允许在一个属性上使用该注解。

属性:

属性名 必填 说明
value SQL生成器实现类
order 枚举类型JKeyOrder,指定主键生成顺序,
默认值为BEFORE,可选值如下:
AFTERinsert后执行SQL
BEFOREinsert前执行SQL

示例:

@JUseSqlGenerator(IdSqlGenerator.class)
private Integer id;

@JUseIdGenerator注解

描述:第五优先级主键策略,通过Java方式生成主键,需要和主键生成器JIdGenerator结合使用,执行插入操作之前会自动将生成的主键注入到对应实体中。

属性:

属性名 必填 说明
value 主键生成器实现类

示例:

@JUseSqlGenerator(IdGenerator.class)
private Integer id;

@JId注解

描述:第六优先级主键策略,仅仅标记当前属性为主键,具体操作由开发者通过代码控制。

示例:

@JId
private String userId;

2.7 动态表名

在多租户的场景下,可能会需要按照租户进行分表,JDBFly允许开发者动态指定表名,前文中的@JTable注解用于指定静态表名,当需要动态表名处理的时候需要使用@JUseDynamicTableName注解。需要注意的是@JUseDynamicTableName注解不能与@JTable注解同时使用,否则动态表名不生效。

JDBFly中默认提供了一种动态表名实现JDynamicTableNameGenerator,需要实体类实现JDynamicTableName接口,在对该实体进行数据库操作时,会将表名修改为实际返回的表名。

@JUseDynamicTableName注解

描述:用于配置指定实体使用动态表名。

属性:

属性名 必填 说明
value 动态表名生成器的实现类
name 默认的表名,等价于使用静态表名的形式
schema 可选schema

示例:

@JUseDynamicTableName(JDynamicTableNameGenerator.class)
public class User {
    // ...
}

2.8 乐观锁

当要修改一条记录的时候,希望与数据库记录同步没有被其他人修改的时候,可以使用JDBFly提供的乐观锁功能。开发者仅需要在开乐观锁的属性上增加@JVersion注解。在使用JDBFly内置方法进行修改操作时,会自动设置新的版本号,需要注意的是在使用乐观锁功能时不应该将乐观锁属性排除查询。

JDBFly内置了一种默认的乐观锁版本号生成器实现JDefaultVersionGenerator,支持的数据类型有:IntegerLongTimestampDateIntegerLong会在原有数据基础上加1TimestampDate会取当前时间。

@JVersion注解

描述:版本号,用于乐观锁,一个实体类中只能存在一个,不能与主键策略一起使用,优先级高于JGeneratedValue

属性:

属性名 必填 说明
value 版本号生成器实现类

示例:

@JVersion
private Integer version;

2.9 自动生成属性值

通常情况下,创建数据库表的时候会冗余创建时间与最后一次修改时间,对应再进行相关插入、修改操作的时候需要手动设置相关时间的数据,JDBFly提供了@JGeneratedValue注解用于自动生成指定属性的数据。需要注意的是@JGeneratedValue注解与主键策略注解或@JVersion注解共同使用无效,仅会在insertupdate操作下生效,且受是否允许添加与修改限制。

JDBFly内置了一种默认的日期类型数据的生成器实现JDateValueGenerator,支持的数据类型有:Datejava.sql.Timestampjava.sql.Datejava.sql.TimeString,使用String数据类型的时候需要结合@JFormat注解指定格式化模板,默认模板为:yyyyMMddHHmmss

@JGeneratedValue注解

描述:自动生成指定属性的数据,与主键策略注解共同使用无效,仅会在insertupdate操作下生效,且受是否允许添加与修改限制。

属性:

属性名 必填 说明
value 数据生成器实现类

示例:

@JGeneratedValue(JDateValueGenerator.class)
private Date createTime;

2.10 自定义Mapper扩展

JDBFly内置通用Mapper不满足开发需求的时候,开发者可以按照规范自定义自己业务相关的Mapper。这里我们用一个内置的Mapper来介绍自定义Mapper扩展的步骤,本质上是一样的,只不过内置Mapper是已经写好的,仅此而已。

2.10.1 编写Mapper接口

我们以JSelectAllMapper为例,首先我们需要创建JSelectAllMapper接口,并增加泛型参数,形如:

public interface JSelectAllMapper<T> {
}

一般情况下,建议开发者在一个Mapper接口中只做一件事情,接下来就需要在刚刚创建的接口中添加对应的Mapper方法。

public interface JSelectAllMapper<T> {
    @JMappedStatementProvider(type = JBaseSelectProvider.class)
    List<T> selectAll();
}

可以看到在定义的selectAll方法上使用了@JMappedStatementProvider注解,该注解用于指定生成此方法XML片段的服务实现类与对应实现方法。@JMappedStatementProvider注解包含两个属性typemethodtype用于指定生成此方法XML片段的服务实现类;method用于指定对应的实现方法,该方法可以是静态方法,也可以是实例方法,但是方法形参必须是(JConfiguration configuration, Class<?> mapperClass, Method method, Document document, Element mapperElement),否则无法解析,method属性的默认值为dynamicMethod,表示动态映射方法,比如例子中服务实现类是JBaseSelectProviderMapper方法名称为selectAll,那么对应的实现方法就是JBaseSelectProvider#selectAll(JConfiguration configuration, Class<?> mapperClass, Method method, Document document, Element mapperElement),若不使用动态映射方法,则可以指定方法名称。

2.10.2 编写XML片段实现方法

有了Mapper接口与实现方法的映射关系,接下来就是编写真正的实现方法。

public class JBaseSelectProvider {
    public void selectAll(JConfiguration configuration, Class<?> mapperClass, Method method, Document document,
            Element mapperElement) {
        JEntity entity = JProviderHelper.resolveEntity(configuration, mapperClass, method);
        String resultMap = JProviderHelper.checkAndCreateResultMap(configuration, document, mapperElement, entity);
        Element selectElement = JElementBuilder.buildSelectElement(document, method, resultMap, null);
        JProviderHelper.selectAllColumns(document, selectElement, entity);
        JProviderHelper.fromTable(configuration, document, selectElement, entity);
        JProviderHelper.orderByDefault(document, selectElement, entity);
        mapperElement.appendChild(selectElement);
    }
}

实现方法的本质上就是通过JavaDom方式编辑Mapper对应的XML,开发者只需要按照方法生成对应的Element之后将其添加到根元素mapperElement中即可。

2.11 自定义MappedStatement构造器

JDBFly内置的构造器支持XML与方法名称的解析,如果实际业务有更加个性化的需求,或者开发者想提供自己的解析方式,JDBFly支持自定义的JMappedStatementBuilder,不过并不建议这样做。

2.11.1 编写构造器实现

public interface JMappedStatementBuilder {
    /**
     * 解析
     * 
     * @param configuration
     * @param mapperClass
     */
    void parse(JConfiguration configuration, Class<?> mapperClass);

    /**
     * 判断是否支持
     * 
     * @param configuration
     * @param mapperClass
     * @return
     */
    boolean support(JConfiguration configuration, Class<?> mapperClass);
}

自定义MappedStatement构造器,开发者需要实现JMappedStatementBuilder接口,并重写parsesupport方法,JDBFly在解析Mapper接口时会从MappedStatement构造器注册器获取所有已注册的MappedStatement构造器,通过support方法验证构造器是否支持对应Mapper接口,如支持则会执行parse真正的解析,即使Mapper接口被构造器解析之后,当查找到后续构造器依然支持解析该Mapper,会再次解析,建议开发者除非有必要对MappedStatement对象进行二次处理,否则应该先判断是否有未解析的方法,避免多构造器重复处理。

String namespace = mapperClass.getName();
if (configuration.isResourceLoaded(namespace)) {
    return;
}
Method[] methods = JMybatisUtils.filterUnParseMethods(configuration, mapperClass);
if (methods == null || methods.length == 0) {
    return;
}

2.11.2 注册构造器实现

开发完自己的构造器之后,需要将构造器加入JDBFly的配置JDBFlyConfiguration中,通过调用JDBFlyConfigurationgetMappedStatementBuilderRegistry()方法可以获得构造器的注册器,通过注册器的addMappedStatementBuilder添加自定义的构造器,构造器名称默认取实现类的简单类名,出现同名时后添加的构造器将覆盖已有构造器。

2.12 自定义XML标签扩展

自定义XML标签用于Mybatis提供的默认标签不满足开发需要的场景,比如在JDBFly中,为了屏蔽数据库的差异,对于一些常用函数进行了XML标签的扩展,这部分标签全部以fn_开头,开发者只需要使用内置的扩展标签即可实现不同数据库的差异屏蔽,当然,JDBFly无法穷举所有的实际开发场景,所以有类似需求的时候,开发者可以通过JDBFly进行原生Mybatis标签的扩展。

2.12.1 编写节点解析器

开发者需要实现JNodeHandler接口来开发自定义节点解析器,该接口有一个需要实现的方法handleNode和一个accept默认方法。

public interface JNodeHandler extends Consumer<JDTD> {
    void handleNode(JXMLScriptBuilder builder, XNode nodeToHandle, List<SqlNode> targetContents);

    @Override
    default void accept(JDTD dtd) {
    }
}

handleNode方法用于解析标签并生成相应的SqlNode,以原生的bind标签的解析器为例:

public class JBindHandler implements JNodeHandler {

    @Override
    public void handleNode(JXMLScriptBuilder builder, XNode nodeToHandle, List<SqlNode> targetContents) {
        final String name = nodeToHandle.getStringAttribute("name");
        final String expression = nodeToHandle.getStringAttribute("value");
        final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
        targetContents.add(node);
    }
}

JBindHandlerder的实现与Mybatis官方的是一致的,只是类名与实现接口改变,通过JDBFly进行自定义XML标签扩展与Mybatis的内置实现相同,通过解析标签属性与内容生成SqlNode,开发者也可以根据实际情况创建自己的SqlNode

accept方法用于对DTD的验证,因为JDBFly扩展了XML标签,在解析XML的时候会对XML文档的合法性进行校验,而自定义标签是Mybatis无法识别的,所以在JDBFly中将静态的DTD文件转变为Java对象,可以动态进行修改,当开发者扩展了XML标签之后,需要实现accept方法以添加或修改为符合实际节点的规则。

public class JFnHandler implements JNodeHandler {

    @Override
    public void handleNode(JXMLScriptBuilder builder, XNode nodeToHandle,
        List<SqlNode> targetContents) {
        JConfiguration configuration = (JConfiguration) builder.getConfiguration();
        JDialect dialect = configuration.getDBFlyConfiguration().getDialect();
        if (!dialect.handleFnNode(builder, nodeToHandle, targetContents)) {
            throw new JDBFlyException("无法解析{}函数标签", nodeToHandle.getName());
        }
    }

    @Override
    public void accept(JDTD dtd) {
        JDTDItem empty = new JDTDEmpty();
        JDTDPCData PCDATA = new JDTDPCData();
        /****** 字符串函数 ******/
        // 参数第一个字符的ASCII码
        dtd.add(new JDTDElement("fn_ascii", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 字符串拼接
        dtd.add(new JDTDElement("fn_concat", new JDTDMixed(new JDTDName("value", JDTDCardinal.ONEMANY))));
        // 返回字符串的字符数
        dtd.add(new JDTDElement("fn_char_length", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 返回字符串的字节数
        dtd.add(new JDTDElement("fn_bit_length", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 返回指定的子字串的位置
        dtd.add(new JDTDElement("fn_position", empty, new JDTDAttribute("sub", "CDATA"),
                new JDTDAttribute("str", "CDATA")));
        // 把字串str里出现地所有子字串from替换成子字串to
        dtd.add(new JDTDElement("fn_replace", empty, new JDTDAttribute("str", "CDATA"),
                new JDTDAttribute("from", "CDATA"), new JDTDAttribute("to", "CDATA")));
        // 从字符串str的start位置截取长度为length的子字符串
        dtd.add(new JDTDElement("fn_sub_str", empty, new JDTDAttribute("str", "CDATA"),
                new JDTDAttribute("start", "CDATA"), new JDTDAttribute("length", "CDATA")));
        // 将字符串转换为大写
        dtd.add(new JDTDElement("fn_upper", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 将字符串转换为小写
        dtd.add(new JDTDElement("fn_lower", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 去掉字符串开始和结尾处的空格
        dtd.add(new JDTDElement("fn_trim", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 去掉字符串开始处的空格
        dtd.add(new JDTDElement("fn_ltrim", PCDATA, new JDTDAttribute("str", "CDATA")));
        // 去掉字符串结尾处的空格
        dtd.add(new JDTDElement("fn_rtrim", PCDATA, new JDTDAttribute("str", "CDATA")));
        /****** 数字函数 ******/
        /****** 日期函数 ******/
        /****** 类型转换函数 ******/
        // 日期格式化
        dtd.add(new JDTDElement("fn_date_format", PCDATA, new JDTDAttribute("format", "CDATA", JDTDDecl.REQUIRED),
                new JDTDAttribute("date", "CDATA")));
        // 字符串转日期
        dtd.add(new JDTDElement("fn_str_to_date", PCDATA, new JDTDAttribute("format", "CDATA", JDTDDecl.REQUIRED),
                new JDTDAttribute("str", "CDATA")));
        /****** 其他函数 ******/
        dtd.add(new JDTDElement("fn_nvl", empty, new JDTDAttribute("exp1", "CDATA", JDTDDecl.REQUIRED),
                new JDTDAttribute("exp2", "CDATA", JDTDDecl.REQUIRED)));
        /****** 辅助标签 ******/
        // 用于分隔参数
        dtd.add(new JDTDElement("value", PCDATA));
        String[] names = { "select", "insert", "selectKey", "update", "delete", "sql", "trim", "where", "set",
                "foreach", "when", "otherwise", "if" };
        for (String name : names) {
            JDTDElement element = dtd.get(name);
            JDTDMixed mixed = (JDTDMixed) element.getContent();
            mixed.add("fn_ascii", "fn_concat", "fn_char_length", "fn_bit_length", "fn_position", "fn_replace",
                    "fn_sub_str", "fn_upper", "fn_lower", "fn_trim", "fn_ltrim", "fn_rtrim", "fn_date_format",
                    "fn_str_to_date", "fn_nvl");
        }
    }
}

2.12.2 注册节点解析器

开发完自己的节点解析器之后,需要将节点解析器设置到JXMLScriptBuilder中,通过调用JXMLScriptBuildersetNodeHandler方法可以将节点解析器加入到脚本构建器中,同时JDBFly也提供了SPI扩展的形式进行注入,开发者可以在META-INF/services/com.jianggujin.dbfly.mybatis.xmltag.JNodeHandler文件中添加解析器实现类。因为节点解析器是针对节点进行解析的,所以在设置节点解析器的时候需要告诉JDBFly解析器可以解析的标签名称,有两种实现方式:

第一种:节点解析器实现JCustomNodeHandler接口的nodeNames方法,返回值为可以解析的XML标签名称数组。

第二种:取节点解析器的简单类名,如果名称以Handler结尾将自动截断,并将类名首字母小写作为可以解析的XML标签名称,如:If -> ifIfHandler -> if

需要注意的是如果多个节点解析器同时支持一个XML标签,后添加的会覆盖之前添加的。

2.12.3 开发阶段验证XML文档

自定义的XML标签在运行阶段解析的时候需要动态生成,防止解析验证失败,同样的,开发好的自定义标签在开发阶段编辑XML的时候,开发工具默认会提供辅助操作,比如智能提示、错误验证等,这时候自定义的标签会出现问题,虽然不影响最终程序运行,但是总会让开发者心情不美丽,这时候可以通过生成新的DTD文件,通过指定URI的方式将XML验证的DTD文件指向自己修改后的文件,避免开发工具直接从网络获取官方DTD文件。开发者可通过如下代码生成最终的修改后的DTD文件。

JDTD dtd = new JDBFlyMapperDTDFactory().createDTD();
dtd.write(new PrintWriter(new FileWriter("mybatis-3-mapper.dtd")));

如果使用XML方式配置Mybatis也可以通过该种方式,只需要将JDBFlyMapperDTDFactory替换为JDBFlyConfigDTDFactory即可

2.12.4 内置扩展标签

2.12.4.1 函数类

2.12.4.1.1 字符串函数
fn_ascii

描述:返回字符串第一个字符的ASCII码。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_ascii>'jianggujin'</fn_ascii> <!-- 106 -->
<fn_ascii str="'jianggujin'"/> <!-- 106 -->
fn_concat

描述:字符串拼接,内部包含value标签,一个标签表示一个待拼接字符串,应至少包含两个非空value标签。

示例:

<fn_concat><value>'jiang'</value><value>'gujin'</value></fn_concat>
<!-- 'jianggujin' -->
fn_char_length

描述:返回字符串的字符数。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_char_length>'jianggujin'</fn_char_length> <!-- 10 -->
<fn_char_length str="'jianggujin'"/> <!-- 10 -->
fn_bit_length

描述:返回字符串的字节数。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_bit_length>'jianggujin'</fn_bit_length> <!-- 10 -->
<fn_bit_length str="'jianggujin'"/> <!-- 10 -->
fn_position

描述:返回字符串在目标字符串中的位置。

属性:

属性名 必填 说明
sub 子字符串内容
str 目标字符串内容

示例:

<fn_position sub=“gu” str=“jianggujin”/> <!-- 6 -->
fn_replace

描述:把字符串str里出现地所有子字符串from替换成子字符串to

属性:

属性名 必填 说明
str 源字符串
from 需要替换的字符串
to 替换的结果字符串

示例:

<fn_replace str=“jianooujin” from="oo" to="gg"/> <!-- 'jianggujin' -->
fn_sub_str

描述:从字符串strstart位置截取长度为length的子字符串。

属性:

属性名 必填 说明
str 待截取的字符串
start 开始位置
`length 截取长度

示例

<fn_sub_str str=“jianggujin” start="6" length="2"/> <!-- 'gu' -->
fn_upper

描述:将字符串转换为大写。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_upper>'jianggujin'</fn_upper> <!-- 'JIANGGUJIN' -->
<fn_upper str="'jianggujin'"/> <!-- 'JIANGGUJIN' -->
fn_lower

描述:将字符串转换为小写。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_lower>'JIANGGUJIN'</fn_lower> <!-- 'jianggujin' -->
<fn_lower str="'JIANGGUJIN'"/> <!-- 'jianggujin' -->
fn_trim

描述:去掉字符串开始和结尾处的空格。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_trim>' jianggujin '</fn_trim> <!-- 'jianggujin' -->
<fn_trim str="' jianggujin '"/> <!-- 'jianggujin' -->
fn_ltrim

描述:去掉字符串开始和结尾处的空格。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_ltrim>' jianggujin '</fn_ltrim> <!-- 'jianggujin ' -->
<fn_ltrim str="' jianggujin '"/> <!-- 'jianggujin ' -->
fn_rtrim

描述:去掉字符串结尾处的空格。

属性:

属性名 必填 说明
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_rtrim>' jianggujin '</fn_rtrim> <!-- ' jianggujin' -->
<fn_rtrim str="' jianggujin '"/> <!-- ' jianggujin' -->
2.12.4.1.2 数字函数
2.12.4.1.3 日期函数
2.12.4.1.4 类型转换函数
fn_date_format

描述:日期格式化。

属性:

属性名 必填 说明
format 字符串内容,属性与元素文本二选一,优先元素文本
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_date_format format="yyyyMMddHHmmss">Now()</fn_date_format>
<fn_date_format format="yyyyMMddHHmmss" date="Now()"/>
fn_str_to_date

描述:字符串转日期

属性:

属性名 必填 说明
format 字符串内容,属性与元素文本二选一,优先元素文本
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_str_to_date format="yyyyMMddHHmmss">'20201106144233'</fn_str_to_date>
<fn_str_to_date format="yyyyMMddHHmmss" str="'20201106144233'"/>
2.12.4.1.2 其他函数
fn_nvl

描述:如果exp1的值不为NULL,则返回exp1,否则返回exp2

属性:

属性名 必填 说明
format 字符串内容,属性与元素文本二选一,优先元素文本
str 字符串内容,属性与元素文本二选一,优先元素文本

示例:

<fn_nvl exp1="amount" exp2="0"/>

2.12.4.2 操作类

page

描述:分页标签

属性:

属性名 必填 说明
name 分页数据对象名称,如果该属性为空,
自动从参数中查找类型为JPage的对象,且其他属性配置无效,
如果该属性设置为:$root,等价于设置:_parameter
startRow 指定开始行属性名称,默认:startRow
endRow 指定结束行属性名称,默认:endRow
pageSize 指定每页数据量属性名称,默认:pageSize

示例:

<page>select id from test</page>
<page name="page">select id from test</page>
alias

描述:别名,自动添加AS alias,屏蔽不同数据库别名差异,例如Oracle数据库会自动为其添加双引号。

属性:

属性名 必填 说明
name 别名,默认取节点内容
as 是否添加AS关键字,默认true,可选值如下:
true:添加
false:不添加

示例:

<alias>name</alias> // => name AS name

2.13 自定义方言扩展

方言是JDBFly中比较重要的部分,主要用于屏蔽数据库差异,提供针对不同数据库的个性化处理。

2.13.1 编写方言实现

编写自己的方言需要实现JDialect接口,对接口中相关方法进行扩展实现即可。

public interface JDialect {

    /**
     * 查询或生成主键的SQL以及生成顺序,如不支持则返回null
     * 
     * @return
     */
    JIdentity getIdentity();

    /**
     * 处理函数节点, 返回{@code true}表示支持并处理,{@code false}表示不支持
     * 
     * @param builder
     * @param nodeToHandle
     * @param targetContents
     * @return
     */
    boolean handleFnNode(JXMLScriptBuilder builder, XNode nodeToHandle, List<SqlNode> targetContents);

    /**
     * 处理分页节点, 返回{@code true}表示支持并处理,{@code false}表示不支持
     * 
     * @param builder
     * @param nodeToHandle
     * @param targetContents
     * @return
     */
    boolean handlePageNode(JXMLScriptBuilder builder, XNode nodeToHandle, List<SqlNode> targetContents);

    /**
     * 处理批量插入,返回{@code true}表示支持并处理,{@code false}表示不支持
     * 
     * @param configuration
     * @param mapperClass
     * @param method
     * @param document
     * @param mapperElement
     * @param entity
     * @return
     */
    boolean handleInsertListMapperMethod(JConfiguration configuration, Class<?> mapperClass, Method method,
            Document document, Element mapperElement, JEntity entity);

    /**
     * 判断当前方言是否支持指定数据库元数据
     * 
     * @return
     * @throws SQLException
     */
    boolean support(DatabaseMetaData metaData) throws SQLException;

}

正常情况下,对方言的扩展实现建议开发者直接继承JAbstarctDialect,在JAbstarctDialect抽象化类中提供了一些通用的处理。

2.13.2 注册方言实现

开发完自己的方言之后,需要将方言加入JDBFly的配置JDBFlyConfiguration中,通过调用JDBFlyConfigurationgetDialectRegistry()方法可以获得方言的注册器,通过注册器的addDialect添加自定义的方言实现,方言名称默认取实现类的简单类名,出现同名时后添加的方言将覆盖已有方言。

除了通过方言注册器添加方言之外,开发者还可以直接通过JDBFlyConfigurationsetDialect方法设置指定的方言实例,二者区别在于直接设置的方言实例将作为后续JDBFly直接使用的方言,否则将通过注册器查找支持的方言实现。

2.14 JDBFly配置说明

JDBFly的特殊配置都是通过JDBFlyConfiguration进行配置,该配置实例可以通过JConfiguration类的getDBFlyConfiguration()方法获得。在JDBFlyConfiguration中可以通过配置的set方法进行设置或者通过Properties的形式进行配置,下面将介绍可以使用的配置属性以及说明。

描述:用于配置Java实体与数据库表、Java实体属性与数据库列之间名称的转换关系。

属性:

属性名 必填 说明
dialect 指定方言实现类,会自动初始化为方言实例
dialects 需要注册的方言实现类,多个类之间使用,分隔,
会将其注册到方言注册器中
style 枚举类型JStyle,表名、属性名列名转换风格,
默认camelhump,可选值如下:
normal:原值
camelhump:驼峰转下划线
uppercase:转换为大写
lowercase:转换为小写
camelhumpAndUppercase:驼峰转下划线大写形式
camelhumpAndLowercase:驼峰转下划线小写形式
useJavaType 是否设置javaType,默认false
notEmpty 是否增加空字符串!=''判断,
仅当Java类型为字符串时生效,默认false
exludeSelects 全局需要排除的不需要查询的列,多个属性之间使用,分隔
excludeInserts 全局需要排除的不需要插入的列,多个属性之间使用,分隔
excludeUpdates 全局需要排除的不需要修改的列,多个属性之间使用,分隔
valueGeneratorProperties 全局Value生成器的列,多个属性之间使用,分隔
valueGeneratorClass 全局Value生成器实现类
schema 全局schema

JDBFly的特殊配置除了可以通过JDBFlyConfiguration配置,在JConfiguration中也提供了快速的配置方法,二者是等价的。

第三部分 数据库版本跟踪

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

JDBFly是一个基于JAVA的持久层开发框架,包含两部分内容:Mybatis增强、数据库版本跟踪。在简化常规开发的同时屏蔽数据库的差异,通过JDBFly使开发者更加关注业务本身,如雄鹰般在天空自由翱翔,从繁琐重复的持久层编码中解放出来。 展开 收起
Java
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/jianggujin/JDBFly.git
git@gitee.com:jianggujin/JDBFly.git
jianggujin
JDBFly
JDBFly
master

搜索帮助