Posts

简易ORM框架

License

  • 此框架适合喜欢JPA注解,但讨厌hibernate和spring data jpa效率低下的.同时又不想失去sql灵活性的.
  • 此框架主要基于ef-orm框架理念
  • 使用jpa注解,但不完全实现jpa规范,单表增,删,改比较方便,同时对级联也做了支持,但不实现延迟加载功能,必须手动调用,才能加载级联对象(此处主要降低jpa实现复杂度).
  • jpa支持注解如下: @Column,@Table,@Entity,@Id,@OneToOne,@OneToMany,@ManyToMany,@ManyToOne.@JoinColumn,@JoinTable,@Version
  • 使用了代码增强技术,增强了实体类.需要继承DBObject类.并使用配置实现代码增强.继承DBObject类的java bean 只要调用set方法即可精确修改数据库对象.
  • 支持级联配置
  • 支持Map格式的数据对象返回(由于不区分字段大小写,要求数据库设计对字段大小写不敏感).
  • 支持使用模板写sql,使用enjoy和jetbrick-template实现.
  • 支持对象操作的乐观锁功能.
  • 支持实体对象生成功能
  • 框架主要基于jdbc实现,极其轻量.几乎全部功能都采用单例模式实现.
  • 整合支持querydsl,jooq用法,提高系统可维护性.能降低80%~90%的sql硬编码.极大提高系统的可维护性.
  • 支持mybatis的resultMap,但无需编写xml映射,实体类只需使用@Column注解和数据库字段映射即可,对于一条sql语句对应一个主类带子类对象,使用@SqlResultSetMapping注解标记即可实现主类、子类的组装.
  • 此框架为整合性框架,感谢ef-orm,jfinal,BeetlSQL,Nutz,mybatis,jetbrick-orm

    快速预览

    1. spring 环境下 引入maven ````
com.github.atshow sorm 1.0.1
配置maven插件
```
<plugin>
    <groupId>com.github.atshow</groupId>
    <artifactId>orm-maven-plugin</artifactId>
    <version>1.0.1</version>
    <executions>
        <execution>
            <goals>
                <goal>enhanceASM</goal>
            </goals>
        </execution>
    </executions>
</plugin>
```

```
    @Bean
	public OrmConfig getOrmConfig(DataSource dataSource) {
	    DaoTemplate dt = new DaoTemplate(dataSource);
		OrmConfig config = new OrmConfig();
		config.setDbClient(dt);
		config.setPackagesToScan(StringUtils.split("db.domain",","));
		config.setDbClient(dt);
		config.setUseTail(true);
		config.setFastBeanMethod(false);
		config.init();
		return config;
	}
	
	@Bean(name="daoTemplate")
	public DaoTemplate geDaoTemplate(OrmConfig config) {
            return (DaoTemplate) config.getDbClient();
    }
```
2. spring boot直接配置
application.properties中配置
```
#jpa实体类所在的包
smallorm.packages=db.domain
...
```
spring boot中的main方法启动中加入增强的代码
```
public static void main(String[] args) throws Exception {
        //jpa实体类所在的包
		new EntityEnhancerJavassist().enhance("db.domain");
		SpringApplication.run(SefApplication.class, args);
	}
```
引入spring-boot-jdbc-starter

3.编写jpa实体类
```
package db.domain;

import sf.database.annotations.Comment;
import sf.database.annotations.FetchDBField;
import sf.database.annotations.Type;
import sf.database.jdbc.extension.ObjectJsonMapping;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.*;
@Entity
@Table(name = "wp_users")
@Comment("用户表")
public class User extends sf.core.DBObject {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "login_name", length = 60, nullable = false)
    private String loginName;// 登陆名

    @Column(length = 64)
    private String password;

    @Column(length = 50)
    private String nicename;

    @Column(length = 100)
    private String email;

    @Column(length = 100)
    private String url;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date registered;

    /**
     * 激活码
     */
    @Column(name = "activation_key", length = 60, nullable = false)
    private String activationKey;

    @Column
    private int status;

    @Column(name = "display_name", length = 250)
    @Enumerated(EnumType.STRING)
    private Names displayName;

    @Column
    private Boolean spam;

    @Column
    private boolean deleted;

    @Column(precision = 10,scale = 5)
    private BigDecimal weight;

    @Transient
    private boolean lock;

    @Column(name = "maps",length = 1500)
    @Type(ObjectJsonMapping.class)
    private Map<String,String> maps;

    @ManyToMany
    @Transient
    @OrderBy("id asc,role desc")
    @JoinTable(name = "user_role", joinColumns = {
            @JoinColumn(name = "user_id", referencedColumnName = "id")}, inverseJoinColumns = {
            @JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<Role> roles;

    @OrderBy
    @Transient
    @FetchDBField({"id","key"})
    @OneToMany(targetEntity = UserMeta.class)
    @JoinColumn(name = "id", referencedColumnName = "userId")
    private Set<UserMeta> userMetaSet = new LinkedHashSet<UserMeta>();

    public enum Names {
        zhangshang, lisi
    }

    /**
     * 普通字段
     */
    public enum Field implements sf.core.DBField {
      id, loginName, password, nicename, email, url, registered, activationKey, status, displayName,maps, spam, deleted,weight;
    }

    /**
     * 级联字段
     */
    public enum CascadeField implements sf.core.DBCascadeField {
        roles, userMetaSet
    }

    public User() {

    }
    ... get set方法
}
```
dao中引入
```
    @Resource
    private DaoTemplate dt;
```
daoTemplate操作sql方法.
* 插入对象
```
User user = dt.selectOne(new User());
User u = new User();
u.setLoginName(UUID.randomUUID().toString());
u.setDeleted(false);
u.setCreated(new Date());
u.setActivationKey("23k4j2k3j4i234j23j4");
//插入对象,生成的语句为:insert into wp_users(activation_key,created,deleted,login_name) values(?,?,?,?)
int i = dt.insert(u);
```
* 执行原生sql
```
String sql = "select * from wp_users";
List<User> list = dt.selectList(User.class, sql);
```
* 执行模板sql
```
#sql("queryUserByName")
select * from wp_users
    #where()
         #if(id)
         and id=#p(id)
         #end

         #if(username)
         and login_name=#p(username)
         #end

         #if(nicename)
         and nicename=#p(nicename)
         #end
         
         #if(nicenames)
         and nicename #in(nicenames)
         #end
    #end
#end
```
java代码
```
Map<String, Object> query = new HashMap<>();
query.put("id", 1);
List<User> list2 = dt.selectListTemplate(User.class, "queryUserByName", query);
```
* 执行Querydsl
```
SQLRelationalPath<User> q = QueryDSLTables.relationalPathBase(User.class);
SQLQuery<User> query = new SQLQuery<User>();
query.select(q).from(q).where(q.string(User.Field.displayName).isNotNull())
        .orderBy(new OrderSpecifier<>(Order.ASC, q.column(User.Field.id)));
Page<User> page = dt.sqlQueryPage(query,User.class, 2, 3);
```
* 执行jooq代码
```
JooqTable<?> quser = JooqTables.getTable(User.class);
JooqTable<?> qrole = JooqTables.getTable(Role.class);
Select<?> query = DSL.select(quser.fields()).from(quser, qrole).where(quser.column(User.Field.id).eq(1));
User u = dt.jooqSelectOne(query,User.class);
```

## 性能测试图
**普通**
![普通性能测试图](doc/img/Dao性能测试对比_common.png "普通")

**打开快速设值开关**
![快速性能测试图](doc/img/Dao性能测试对比_fast.png "快速")

##2018-12-15 16:17:51 更新
* 1.支持对数据库关键字无需使用标识符.

## 一、概述
### Mybatis的问题
  引用大牛的话:Mybatis最大的问题不在于开发效率,而在维护效率上。其过于原生的数据库操作方式,难以避免项目维护过程中的巨大成本。
当数据库字段变化带来的修改工作虽然可以集中到少数几个XML文件中,但是依然会分散在文件的各处,并且你无法依靠Java编译器帮助你发现这些修改是否有错漏。
在一个复杂的使用Mybatis的项目中,变更数据库结构往往带来大量的CodeReview和测试工作,否则难以保证项目的稳定性。
* 1、关联表多时,字段多的时候,sql工作量很大。
* 2sql依赖于数据库,导致数据库移植性差。
* 3、由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
* 4、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql
* 5DAO层过于简单,对象组装的工作量较大。
* 6、不支持级联更新、级联删除。
* 7Mybatis的日志除了基本记录功能外,其它功能薄弱很多。
* 8、编写动态sql,不方便调试,尤其逻辑复杂时。
* 9、提供的写动态sqlxml标签功能简单,编写动态sql仍然受限,且可读性低。

### Hibernate的问题
* 概念复杂
* 性能不佳
* 优化困难.

### 额外说明
1. 框架在JPA实体类注解基础上,增加枚举字段,用于表示具体的数据库字段和级联字段.
2. 框架使用了静态代码增加技术.做到了真正的实体类值有变化才执行DML(,,)操作
3. 为了兼容各数据库,框架对Map类型的返回,不区分字段大小写,此处要求数据库设计对字段大小写不敏感
4. 为了最大程度支持数据库查询的灵活性,所有的返回值由你决定(实体类,Java基本类型,String,Map,List,Object[]等类型).
5. 框架支持Sqlserver2005及以上版本,Oracle,MySQL,PostgreSQL,SQLite 数据库.

## 二、实体操作
### 实体类继承
为了实现实体的动态更新,数据实体类需要继承:sf.core.DBObject
```
public class XXX extends sf.core.DBObject 
```
对于的数据字段需要实现继承:sf.core.DBField接口的枚举
```
public enum Field implements sf.core.DBField{
    XXX
}
```
具体可以参数快速开发的中的User类以及sorm-test工程.
此处是为解析表结构做准备,对于数据字段的枚举描述,可以看到后面的querydsljooq依赖这些字段.
### 实体类增强.
在上面的例子中,还可以看到spring boot中的代码增强:
```
new EntityEnhancerJavassist().enhance("db.domain");
```
该代码主要是使用javassit对继承了DBObject的实体类做了静态代码增强.框架也同时提供基于ASM的实现
使用maven构建时,可以配置Maven-Plugin,使其在编译完后自动扫描编译路径并执行增强操作。请使用:
com.github.atshow orm-maven-plugin 1.0 enhanceASM
### 单表操作
#### 编写jpa实体类
```
package db.domain;

import sf.database.annotations.Comment;
import sf.database.annotations.FetchDBField;
import sf.database.annotations.Type;
import sf.database.jdbc.extension.ObjectJsonMapping;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.*;
@Entity
@Table(name = "wp_users")
@Comment("用户表")
public class User extends sf.core.DBObject {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "login_name", length = 60, nullable = false)
    private String loginName;// 登陆名

    @Column(length = 64)
    private String password;

    @Column(length = 50)
    private String nicename;

    @Column(length = 100)
    private String email;

    @Column(length = 100)
    private String url;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date registered;

    /**
     * 激活码
     */
    @Column(name = "activation_key", length = 60, nullable = false)
    private String activationKey;

    @Column
    private int status;

    @Column(name = "display_name", length = 250)
    @Enumerated(EnumType.STRING)
    private Names displayName;

    @Column
    private Boolean spam;

    @Column
    private boolean deleted;

    @Column(precision = 10,scale = 5)
    private BigDecimal weight;

    @Transient
    private boolean lock;

    @Column(name = "maps",length = 1500)
    @Type(ObjectJsonMapping.class)
    private Map<String,String> maps;

    @ManyToMany
    @Transient
    @OrderBy("id asc,role desc")
    @JoinTable(name = "user_role", joinColumns = {
            @JoinColumn(name = "user_id", referencedColumnName = "id")}, inverseJoinColumns = {
            @JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<Role> roles;

    @OrderBy
    @Transient
    @FetchDBField({"id","key"})
    @OneToMany(targetEntity = UserMeta.class)
    @JoinColumn(name = "id", referencedColumnName = "userId")
    private Set<UserMeta> userMetaSet = new LinkedHashSet<UserMeta>();

    public enum Names {
        zhangshang, lisi
    }

    /**
     * 普通字段
     */
    public enum Field implements sf.core.DBField {
      id, loginName, password, nicename, email, url, registered, activationKey, status, displayName,maps, spam, deleted,weight;
    }

    /**
     * 级联字段
     */
    public enum CascadeField implements sf.core.DBCascadeField {
        roles, userMetaSet
    }

    public User() {

    }
    ... get set方法
}
```

#### 创建Dao操作类
```
// 创建一个数据源
SimpleDataSource dataSource = new SimpleDataSource();
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1/nutzdemo");
dataSource.setUsername("root");
dataSource.setPassword("root");

// 创建一个DBClient实例,在真实项目中, DBClient通常由Spring托管, 使用注入的方式获得.
DBClient dao = new DBClient(dataSource);

// 创建表
dao.createTable(User.class);//如果存在该表则不创建.

User user = new User();
user.setLoginName("ABC");
user.setNicename("CDF");
dao.insert(user);
System.out.println(p.getId());
```

1. 插入和批量插入
对应DBClient中的方法为
```
dao.insert(user);//使用该方法插入后,如果user中的主键将自动被写入user对象中.
//批量插入
List<User> modelList = new ArrayList<>();
....
dao.batchInsert(modelList);
```
除上面的用法,框架也提供了快速的插入和快速批量插入的方法(快速插入都不返回主键)
```
dao.insertFast(user);
//批量插入
List<User> modelList = new ArrayList<>();
....
dao.batchInsertFast(modelList);
```

2. 修改和批量修改
```
//更新对象,如果有查询sql,则按查询sql更新对象(优先级高);如果有主键则按主键查询(优先级低).
User u = new User();
u.setId(1L);
u.setNicename("asdfds");
u.useQuery().createCriteria().eq(User.Field.id,1).and().eq(User.Field.displayName,"222");
dao.update(u);

//批量更新,只支持按主键更新,所以必须设置主键,且所有的需要更新的对象中的属性必须一致.框架使用
//第一个对象中的属性,生成批量更新的执行sql.
List<User> modelList = new ArrayList<>();
....
dao.batchUpdate(modelList);
```

3. 删除和批量删除
```
//删除对象.如果有查询sql,则按查询sql删除对象(优先级高);如果无查询sql,将根据设置的属性,生成删除的sql,使用时请注意.
User u = new User();
u.setId(1L);
u.setNicename("asdfds");
dao.delete(u);
```
或者
```
User u = new User();
u.useQuery().createCriteria().eq(User.Field.id,1).and().eq(User.Field.displayName,"222");
dao.delete(u);
```
```
//批量对象,将根据设置的属性生成删除sql语句,且所有的需要删除的对象中的属性必须一致.框架使用
//第一个对象中的属性,生成批量删除的执行sql.
List<User> modelList = new ArrayList<>();
....
dao.batchDelete(modelList);
```

4. 乐观锁
当实体对象中,有字段使用JAP注解:@Version标注时,框架将为该字段启用乐观锁控制.<br>
支持:整数型,日期类型和字符串类型(字符串类型使用UUID实现)的乐观锁功能.<br>
默认的insert,update方法已经提供了乐观锁功能.另外,框架也提供了其他操作乐观锁的功能.

//具体可以查看api注释.

int updateAndSet(T obj); int updateWithVersion(T obj); ```` 5. 查询 针对model的查询,只需要在实体类中,设置过值,即可. ``` /** * 根据主键查询 * @param clz 实体类 * @param keyParams 主键参数 * @param 泛型 * @return 实体 */ T selectByPrimaryKeys(Class clz, Object... keyParams); /** * 查询总数 * @param query 查询 * @param 泛型 * @return 实体 */ long selectCount(T query); /** * 查询一条记录,如果结果不唯一则抛出异常 * @param query 查询条件 * @return 查询结果 */ T selectOne(T query); /** * 使用select ... for update 查询数据 * @param query 查询条件 * @param 泛型 * @return 实体 */ T selectOneForUpdate(T query); /** * 查询列表 * @param query 查询请求。 *
    *
  • 如果设置了Query条件,按query条件查询。 否则——
  • *
  • 如果设置了主键值,按主键查询,否则——
  • *
  • 按所有设置过值的字段作为条件查询。
  • *
* @return 结果 */ List selectList(T query); /** * 使用select ... for update 查询数据 * @param query 查询 * @param 泛型 * @return 实体 */ List selectListForUpdate(T query); /** * 查询并分页 * @param query 查询请求 * @param start 起始记录,offset。从0开始。 * @param limit 限制记录条数。如每页10条传入10。 * @return 分页对象 */ Page selectPage(T query, int start, int limit); /** * 查询迭代结果.回调形式. * @param ormIt 迭代回调方法 * @param query 查询 * @param 泛型 */ void selectIterator(OrmIterator ormIt, T query); /** * 查询限制条数和起始位置的迭代结果.回调形式. * @param ormIt 迭代回调方法 * @param query 查询 * @param start 起始数 * @param limit 限制数 * @param 泛型 */ void selectIterator(OrmIterator ormIt, T query, int start, int limit); /** * stream lambda形式迭代结果. * @param ormStream 迭代回调方法 * @param query 查询 * @param 泛型 */ void selectStream(OrmStream ormStream, T query); ``` ### 级联操作 1. 级联关系配置 框架使用jpa注解,级联配置基本上和hibernate一样,唯一的区别在于,级联字段都需要使用Java字段名称,而不是数据库列名. 在jpa注解之外,还添加了额外的几个注解. ``` //此注解说明,需要抓取的级联对象的字段.主要是适用于,无需全部查询级联对象字段的值 @FetchDBField ``` 2. 级联对象插入 框架未提供单独的级联对象插入功能,可以使用普通的对象插入方法,保存级联对象. 3. 级联对象修改 级联对象修改,需要在主对象完整的情况下.使用: ``` /** * 将对象插入数据库同时,也将指定级联字段的所有关联字段关联的对象统统插入相应的数据库 *

* 关于关联字段更多信息,请参看 '@One' | '@Many' | '@ManyMany' 更多的描述 * @param obj * @param fields 指定字段,控制力度更细,至少一个或多个 描述了什么样的关联字段将被关注。如果为 null,则表示全部的关联字段都会被插入 * @return */ int insertCascade(DBObject obj, DBCascadeField... fields); /** * 仅将对象所有的关联字段插入到数据库中,并不包括对象本身 * @param obj 数据对象 * @param fields 字段名称,描述了什么样的关联字段将被关注。如果为 null,则表示全部的关联字段都会被插入 * @return 数据对象本身 * @see javax.persistence.OneToOne * @see javax.persistence.ManyToMany * @see javax.persistence.OneToMany */ T insertLinks(T obj, DBCascadeField... fields); /** * 将对象的一个或者多个,多对多的关联信息,插入数据表 * @param obj 对象 * @param fields 正则表达式,描述了那种多对多关联字段将被执行该操作 * @return 对象自身 * @see javax.persistence.ManyToMany */ T insertRelation(T obj, DBCascadeField... fields); ``` 4. 级联对象删除 ``` /** * 将对象删除的同时,也将指定级联字段的所有关联字段关联的对象统统删除 <b style=color:red>注意:</b> *

* Java 对象的字段会被保留,这里的删除,将只会删除数据库中的记录 *

* 关于关联字段更多信息,请参看 '@One' | '@Many' | '@ManyMany' 更多的描述 * @param obj 对象 * @param fields 指定字段,控制力度更细,至少一个或多个 描述了什么样的关联字段将被关注。如果为 null,则表示全部的关联字段都会被删除 * @param 泛型 * @return 执行结果 */ int deleteCascade(T obj, DBCascadeField... fields); /** * 仅删除对象所有的关联字段,并不包括对象本身。 <b style=color:red>注意:</b> *

* Java 对象的字段会被保留,这里的删除,将只会删除数据库中的记录 *

* 关于关联字段更多信息,请参看 '@One' | '@Many' | '@ManyMany' 更多的描述 * @param obj 数据对象 * @param fields 字段名称,描述了什么样的关联字段将被关注。如果为 null,则表示全部的关联字段都会被删除 * @return 被影响的记录行数 * @see javax.persistence.OneToOne * @see javax.persistence.ManyToOne * @see javax.persistence.ManyToMany */ int deleteLinks(T obj, DBCascadeField... fields); /** * 多对多关联是通过一个中间表将两条数据表记录关联起来。 *

* 而这个中间表可能还有其他的字段,比如描述关联的权重等 *

* 这个操作可以让你一次删除某一个对象中多个多对多关联的数据 * @param obj * @param fields 字段名称,描述了那种多对多关联字段将被执行该操作 * @return 共有多少条数据被更新 * @see javax.persistence.ManyToMany */ int deleteRelation(T obj, DBCascadeField... fields); ``` 5. 级联对象查询 ``` /** * 查找对象列表,并查询级联字段 * @param query 查询 * @param clz 实体类 * @param fields 级联字段 * @param 泛型 * @return 列表 */ List fetchCascade(T query, Class clz, DBCascadeField... fields); /** * 查询对象,并返回所有的级联字段的值 * @param obj 实体 * @return 返回带级联字段值得对象 */ T fetchLinks(T obj); /** * 查询单一对象 * @param obj 实体类 * @param fields 级联字段 * @return 对象 */ T fetchLinks(T obj, DBCascadeField... fields); ``` ## 三、sql操作 此大类方法主要是为了兼容普通jdbc操作,使框架与JdbcTemplate使用一致. 1. 自由设置返回值,此功能主要是改进mybatis中的resultMap书写不方便的问题. 对于实体类,当实体类的字段与sql语句返回的字段不一致时,可以使用@Column注解的name值,设置对应的返回字段. 具体请查看DBMethod类中的方法. 2. 支持查询对象拆分到更多的子对象中. 此功能只需要在实体类中,添加一对一的映射,使用JPA的@SqlResultSetMapping注解. ```` //此注解框架实现:主要解决子对象的创建问题. //此处实现:只取第一个EntityResult的配置,并且只识别fields类型字段 @SqlResultSetMapping(name = "UserResult", entities = { @EntityResult(entityClass = UserResult.class, fields = { @FieldResult(name = "metaResult.userId", column = "id"/*对应别名*/), @FieldResult(name = "metaResult.icon", column = "icon"/*对应别名*/) } ) } ) public class UserResult { private Long id; @Column(name = "login_name") private String loginName;// 登陆名 private String password; private String nicename; private MetaResult metaResult; ..... 省略get set ```` ```` public class MetaResult { private Long userId; private String icon; ..... 省略get set ```` ## 四、模板操作 模板主要使用enjoy模板实现,基本功能和jfinal中的enjoy sql模板一致,框架主要额外添加了如下指令 1. where ``` #where() #if(id) and id=#p(id) #end #if(username) and login_name=#p(username) #end #if(nicename) and nicename=#p(nicename) #end #if(nicenames) and nicename #in(nicenames) #end #end ``` 2. in in中的变量值为Collection类型 ```` #if(nicenames) and nicename #in(nicenames) #end ```` 3. pageTag pageTag与pageIgnore主要为分页服务. page和pageTag区别: 如果无参数,则在查询的时候解释成 *,如果有参数,则解释成列名,如 page("a.name,a.id,b.name role_name") ,如果列名较多,可以使用pageTag ```` #sql("queryUserByNamePage") select #pageTag("*") from wp_users #where() #if(id) and id=#p(id) #end #if(username) and login_name=#p(username) #end #if(nicename) and nicename=#p(nicename) #end #end #pageIgnoreTag() order by id #end #end ```` 4. pageIgnore 标签函数 pageIgnoreTag,用在翻页查询里,当查询用作统计总数的时候,会忽略标签体内容 5. use use主要用于复用sql片段. ```` #sql("user_cols") id,login_name,nicename #end #sql("queryUserByName") select #use("user_cols") from wp_users #use("user_condition") #end ```` ## 五、整合QueryDSL和jOOQ 整合QueryDSL和JOOQ主要是为了给框架提供类型安全的查询语句生成,避免代码里出现过多的硬编码sql语句,导致维护噩梦. 使用QueryDSL和JOOQ主要以使用固定表为主. 1. QueryDSL 首先需要继承DBObject的数据库实体类, ``` //此处主要是通过QueryDSLTables获取Q类,QueryDSLTables提供缓存功能. SQLRelationalPath q = QueryDSLTables.relationalPathBase(User.class); SQLQuery query = new SQLQuery(); query.select(q).from(q).where(q.string(User.Field.displayName).isNotNull()) .orderBy(new OrderSpecifier<>(Order.ASC, q.column(User.Field.id))); Page page = dt.queryDSLSelectPage(query,User.class, 2, 3); ``` 2. Jooq 主要支持开源的jooq版本,对于商业版本,可以在生成DML语句时,使用DSL.using(Dialect)解决,框架将以设置的Dialect执行语句. ``` //此处主要是通过JooqTables获取Q类,JooqTables提供缓存功能. JooqTable quser = JooqTables.getTable(User.class); JooqTable qrole = JooqTables.getTable(Role.class); Select<?> query = DSL.select(quser.fields()).from(quser, qrole).where(quser.column(User.Field.id).eq(1)); User u = dt.jooqSelectOne(query,User.class); ``` ## 六、和spring,spring boot整合 Spring主要使用DaoTemplate类,此类已于Spring jdbc框架做了集成. 1.和spring 整合 ``` //多数据源配置 @Bean public OrmConfig getOrmConfig(@Autowired(required = false) @Qualifier("xaMysql") DataSource xaMysql, @Autowired(required = false) @Qualifier("sqlite") DataSource sqlite, @Autowired(required = false) @Qualifier("postgresql") DataSource postgresql, @Autowired(required = false) @Qualifier("oracle") DataSource oracle, @Autowired(required = false) @Qualifier("sqlserver") DataSource sqlserver) { SimpleRoutingDataSource routing = new SimpleRoutingDataSource(); routing.getDataSources().put("mysql", sqlite); routing.getDataSources().put("sqlite", sqlite); // routing.addDataSource("postgresql", postgresql); routing.setDefaultKey("mysql"); DaoTemplate dt = new DaoTemplate(routing); // DaoTemplate dt = new DaoTemplate(xaMysql); OrmConfig config = OrmConfig.getInstance(); config.setDbClient(dt); //需要扫描的实体类.此处提供自动建表功能. config.setPackagesToScan(StringUtils.split("db.domain", ",")); config.setUseTail(true); config.setBeanValueType(OrmValueUtils.BeanValueType.fast); config.setUseSystemPrint(false); config.init(); return config; } @Bean(name = "daoTemplate") public DaoTemplate geDaoTemplate(OrmConfig config) { return (DaoTemplate) config.getDbClient(); } ``` 2.和spring boot 整合. 以下属性允许在application.properties中配置. ``` smallorm.packages=db.domain #指定扫描若干包 smallorm.show-sql=true #sql日志开关 smallorm.bean-value-type=method #使用哪一种bean转换,method,field,unsafe smallorm.use-tail=false #是否使用tail获取额外属性. smallorm.allow-drop-column=false #扫描到实体后,如果准备修改表,如果数据库中的列多余,是否允许删除列 smallorm.create-table=true #扫描到实体后,如果数据库中不存在,是否建表 smallorm.alter-table=true #扫扫描到实体后,如果数据库中存在对应表,是否修改表 smallorm.batch-size=100 #批处理数量 smallorm.enhance-scan-packages=true #对配置了包扫描的路径进行增强检查,方便单元测试 smallorm.open-stream-iterator=false #是否开启流式迭代 smallorm.sql-template-path=classpath*:sql/**/*.sql #sql模板位置 smallorm.sql-template-debug=false #是否开启sql模板调试模式 smallorm.sql-template-type=enjoy #使用的sql模板类型 ``` ## 七、其他功能 1. 多数据源,DBClient中使用如下方法,即可切换数据源. ``` /** * 执行sql上下文,比如切换数据源 * @param dataSource 数据源名称 * @return DBClient */ DBClient useContext(String dataSource); ``` 2. 代码生成 ``` /** * 根据表名生成对应的pojo类 * @param pkg 包名,如 com.test * @param srcPath: 文件保存路径 * @param config 配置生成的风格 */ void genPojoCodes(String pkg, String srcPath, GenConfig config); ``` 3. 分库分表功能 请使用第三方如:sharing-jdbc,mycat等整合.

subscribe via RSS