JDBFly使用教程

第一部分 简介

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项目集成
  • 数据库版本跟踪:提供通用数据类型,无需关心具体数据库数据映射关系,对数据库版本自动维护升级

1.2 支持数据库

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

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的坐标:

<!-- 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代码段
IsNullcolumn is null
IsNotNullcolumn is not null
EqualToEtcolumn = value
NotEqualToNecolumn <> value
GreaterThanGtcolumn > value
GreaterThanOrEqualToGecolumn >= value
LessThanLtcolumn < value
LessThanOrEqualToLecolumn <= value
Incolumn in (value)
NotIncolumn not in (value)
Betweencolumn between value1 and value2
NotBetweencolumn not between value1 and value2
Likecolumn like value
LikeLeftcolumn like '%value'
LikeRightcolumn like 'value%'
NotLikecolumn not like value
NotLikeLeftcolumn not like '%value'
NotLikeRightcolumn 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,同一个实体类中只允许在一个属性上使用该注解。

属性:

属性名必填说明
valueSQL生成器实现类
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

@JVersion注解

描述:自动生成指定属性的数据,与主键策略注解共同使用无效,仅会在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解析器可以解析的标签名称,有两种实现方式:

第一种:节点解析器实现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中也提供了快速的配置方法,二者是等价的。

第三部分 数据库版本跟踪

第四部分 附录

附录一:扩展Mapper的DTD文件

<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select*)+>
<!ATTLIST mapper
namespace CDATA #IMPLIED
>

<!ELEMENT cache-ref EMPTY>
<!ATTLIST cache-ref
namespace CDATA #REQUIRED
>

<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
blocking CDATA #IMPLIED
>

<!ELEMENT parameterMap (parameter+)?>
<!ATTLIST parameterMap
id CDATA #REQUIRED
type CDATA #REQUIRED
>

<!ELEMENT parameter EMPTY>
<!ATTLIST parameter
property CDATA #REQUIRED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
mode (IN | OUT | INOUT) #IMPLIED
resultMap CDATA #IMPLIED
scale CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>

<!ELEMENT resultMap (constructor?,id*,result*,association*,collection*,discriminator?)>
<!ATTLIST resultMap
id CDATA #REQUIRED
type CDATA #REQUIRED
extends CDATA #IMPLIED
autoMapping (true | false) #IMPLIED
>

<!ELEMENT constructor (idArg*,arg*)>

<!ELEMENT id EMPTY>
<!ATTLIST id
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>

<!ELEMENT result EMPTY>
<!ATTLIST result
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>

<!ELEMENT idArg EMPTY>
<!ATTLIST idArg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
name CDATA #IMPLIED
>

<!ELEMENT arg EMPTY>
<!ATTLIST arg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
name CDATA #IMPLIED
>

<!ELEMENT collection (constructor?,id*,result*,association*,collection*,discriminator?)>
<!ATTLIST collection
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
ofType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true | false) #IMPLIED
fetchType (lazy | eager) #IMPLIED
>

<!ELEMENT association (constructor?,id*,result*,association*,collection*,discriminator?)>
<!ATTLIST association
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true | false) #IMPLIED
fetchType (lazy | eager) #IMPLIED
>

<!ELEMENT discriminator (case+)>
<!ATTLIST discriminator
column CDATA #IMPLIED
javaType CDATA #REQUIRED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>

<!ELEMENT case (constructor?,id*,result*,association*,collection*,discriminator?)>
<!ATTLIST case
value CDATA #REQUIRED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
>

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
alias CDATA #REQUIRED
type CDATA #REQUIRED
>

<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT | PREPARED | CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true | false) #IMPLIED
useCache (true | false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true | false) #IMPLIED
resultSets CDATA #IMPLIED
>

<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true | false) #IMPLIED
statementType (STATEMENT | PREPARED | CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true | false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

<!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST selectKey
resultType CDATA #IMPLIED
statementType (STATEMENT | PREPARED | CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
keyColumn CDATA #IMPLIED
order (BEFORE | AFTER) #IMPLIED
databaseId CDATA #IMPLIED
>

<!ELEMENT update (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true | false) #IMPLIED
statementType (STATEMENT | PREPARED | CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true | false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

<!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true | false) #IMPLIED
statementType (STATEMENT | PREPARED | CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

<!ELEMENT include (property+)?>
<!ATTLIST include
refid CDATA #REQUIRED
>

<!ELEMENT bind EMPTY>
<!ATTLIST bind
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST sql
id CDATA #REQUIRED
lang CDATA #IMPLIED
databaseId CDATA #IMPLIED
>

<!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST trim
prefix CDATA #IMPLIED
prefixOverrides CDATA #IMPLIED
suffix CDATA #IMPLIED
suffixOverrides CDATA #IMPLIED
>

<!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>

<!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>

<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST foreach
collection CDATA #REQUIRED
item CDATA #IMPLIED
index CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
separator CDATA #IMPLIED
>

<!ELEMENT choose (when*,otherwise?)>

<!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST when
test CDATA #REQUIRED
>

<!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>

<!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias | page)*>
<!ATTLIST if
test CDATA #REQUIRED
>

<!ELEMENT fn_ascii (#PCDATA)>
<!ATTLIST fn_ascii
str CDATA #IMPLIED
>

<!ELEMENT fn_concat (value+)>

<!ELEMENT fn_char_length (#PCDATA)>
<!ATTLIST fn_char_length
str CDATA #IMPLIED
>

<!ELEMENT fn_bit_length (#PCDATA)>
<!ATTLIST fn_bit_length
str CDATA #IMPLIED
>

<!ELEMENT fn_position EMPTY>
<!ATTLIST fn_position
sub CDATA #IMPLIED
str CDATA #IMPLIED
>

<!ELEMENT fn_replace EMPTY>
<!ATTLIST fn_replace
str CDATA #IMPLIED
from CDATA #IMPLIED
to CDATA #IMPLIED
>

<!ELEMENT fn_sub_str EMPTY>
<!ATTLIST fn_sub_str
str CDATA #IMPLIED
start CDATA #IMPLIED
length CDATA #IMPLIED
>

<!ELEMENT fn_upper (#PCDATA)>
<!ATTLIST fn_upper
str CDATA #IMPLIED
>

<!ELEMENT fn_lower (#PCDATA)>
<!ATTLIST fn_lower
str CDATA #IMPLIED
>

<!ELEMENT fn_trim (#PCDATA)>
<!ATTLIST fn_trim
str CDATA #IMPLIED
>

<!ELEMENT fn_ltrim (#PCDATA)>
<!ATTLIST fn_ltrim
str CDATA #IMPLIED
>

<!ELEMENT fn_rtrim (#PCDATA)>
<!ATTLIST fn_rtrim
str CDATA #IMPLIED
>

<!ELEMENT fn_date_format (#PCDATA)>
<!ATTLIST fn_date_format
format CDATA #REQUIRED
date CDATA #IMPLIED
>

<!ELEMENT fn_str_to_date (#PCDATA)>
<!ATTLIST fn_str_to_date
format CDATA #REQUIRED
str CDATA #IMPLIED
>

<!ELEMENT fn_nvl EMPTY>
<!ATTLIST fn_nvl
exp1 CDATA #REQUIRED
exp2 CDATA #REQUIRED
>

<!ELEMENT value (#PCDATA)>

<!ELEMENT alias (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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)*>
<!ATTLIST alias
name CDATA #IMPLIED
as (true | false) #IMPLIED
>

<!ELEMENT page (#PCDATA | include | trim | where | set | foreach | choose | if | bind | 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 | alias)*>
<!ATTLIST page
name CDATA #IMPLIED
startRow CDATA #IMPLIED
endRow CDATA #IMPLIED
pageSize CDATA #IMPLIED
>

附录二:扩展Config的DTD文件

<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT configuration (properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?,dbfly?)>

<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT settings (setting+)>

<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT typeAliases (typeAlias*,package*)>

<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
>

<!ELEMENT typeHandlers (typeHandler*,package*)>

<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>

<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>

<!ELEMENT objectWrapperFactory EMPTY>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>

<!ELEMENT reflectorFactory EMPTY>
<!ATTLIST reflectorFactory
type CDATA #REQUIRED
>

<!ELEMENT plugins (plugin+)>

<!ELEMENT plugin (property*)>
<!ATTLIST plugin
interceptor CDATA #REQUIRED
>

<!ELEMENT environments (environment+)>
<!ATTLIST environments
default CDATA #REQUIRED
>

<!ELEMENT environment (transactionManager,dataSource)>
<!ATTLIST environment
id CDATA #REQUIRED
>

<!ELEMENT transactionManager (property*)>
<!ATTLIST transactionManager
type CDATA #REQUIRED
>

<!ELEMENT dataSource (property*)>
<!ATTLIST dataSource
type CDATA #REQUIRED
>

<!ELEMENT mappers (mapper*,package*)>

<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>

<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>

<!ELEMENT dbfly (dialect?,style?,useJavaType?,notEmpty?,exludeSelects?,excludeInserts?,excludeUpdates?,valueGeneratorProperties?,valueGeneratorClass?)>

<!ELEMENT dialect EMPTY>
<!ATTLIST dialect
name CDATA #REQUIRED
>

<!ELEMENT style EMPTY>
<!ATTLIST style
name (normal | camelhump | uppercase | lowercase | camelhumpAndUppercase | camelhumpAndLowercase) "camelhump"
>

<!ELEMENT useJavaType EMPTY>
<!ATTLIST useJavaType
value (true | false) "false"
>

<!ELEMENT notEmpty EMPTY>
<!ATTLIST notEmpty
value (true | false) "false"
>

<!ELEMENT exludeSelects EMPTY>
<!ATTLIST exludeSelects
property CDATA #REQUIRED
>

<!ELEMENT excludeInserts EMPTY>
<!ATTLIST excludeInserts
property CDATA #REQUIRED
>

<!ELEMENT excludeUpdates EMPTY>
<!ATTLIST excludeUpdates
property CDATA #REQUIRED
>

<!ELEMENT valueGeneratorProperties EMPTY>
<!ATTLIST valueGeneratorProperties
property CDATA #REQUIRED
>

<!ELEMENT valueGeneratorClass EMPTY>
<!ATTLIST valueGeneratorClass
name CDATA #REQUIRED
>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值