百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

简易 ORM 框架 sorms(简述orm框架的原理,列举几个常见的orm框架)

ccwgpt 2024-10-16 08:00 30 浏览 0 评论

简易ORM

  • 适合喜欢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注解标记即可实现主类、子类的组装.
  • 此为整合性orm,感谢ef-orm,jfinal,BeetlSQL,Nutz,mybatis,jetbrick-orm
  • 最低要求jdk8,兼容jdk11.

快速预览

  1. spring 环境下 引入maven
<!-- 由于版本还在完善中,暂时先使用私仓 -->
<repository>
 <id>sxfmaven</id>
 <name>local private nexus</name>
 <url>https://gitee.com/parken/sxfmvnrepo/raw/master/</url>
 <releases>
 <enabled>true</enabled>
 </releases>
 <snapshots>
 <enabled>false</enabled>
 </snapshots>
</repository>
<!-- 引入jar包 -->
<dependency>
 <groupId>com.github.atshow</groupId>
 <artifactId>sorm</artifactId>
 <version>1.0.1</version>
</dependency>

配置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();
 }
  1. 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);

性能测试图

普通

打开快速设值开关

##2018-12-15 16:17:51 更新

  • 1.支持对数据库关键字无需使用标识符.

一、概述

Mybatis的问题

引用大牛的话:Mybatis最大的问题不在于开发效率,而在维护效率上。其过于原生的数据库操作方式,难以避免项目维护过程中的巨大成本。 当数据库字段变化带来的修改工作虽然可以集中到少数几个XML文件中,但是依然会分散在文件的各处,并且你无法依靠Java编译器帮助你发现这些修改是否有错漏。 在一个复杂的使用Mybatis的项目中,变更数据库结构往往带来大量的CodeReview和测试工作,否则难以保证项目的稳定性。

  • 1、关联表多时,字段多的时候,sql工作量很大。
  • 2、sql依赖于数据库,导致数据库移植性差。
  • 3、由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
  • 4、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。
  • 5、DAO层过于简单,对象组装的工作量较大。
  • 6、不支持级联更新、级联删除。
  • 7、Mybatis的日志除了基本记录功能外,其它功能薄弱很多。
  • 8、编写动态sql时,不方便调试,尤其逻辑复杂时。
  • 9、提供的写动态sql的xml标签功能简单,编写动态sql仍然受限,且可读性低。

Hibernate的问题

  • 概念复杂
  • 性能不佳
  • 优化困难.
  • 过度设计

额外说明

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

二、实体操作

实体类继承

为了实现实体的动态更新,数据实体类需要继承:sf.core.DBObject

public class XXX extends sf.core.DBObject 

对于的数据字段需要实现继承:sf.core.DBField接口的枚举

public enum Field implements sf.core.DBField{
 XXX
}

具体可以参数快速开发的中的User类以及sorm-test工程. 此处是为解析表结构做准备,对于数据字段的枚举描述,可以看到后面的querydsl和jooq集成依赖这些字段.

实体类增强.

在上面的例子中,还可以看到spring boot中的代码增强:

new EntityEnhancerJavassist().enhance("db.domain");

该代码主要是使用javassit对继承了DBObject的实体类做了静态代码增强.也同时提供基于ASM的实现 使用maven构建时,可以配置Maven-Plugin,使其在编译完后自动扫描编译路径并执行增强操作。请使用:

 <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>

单表操作

编写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);
  1. 修改和批量修改
//更新对象,如果有查询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);
  1. 删除和批量删除
//删除对象.如果有查询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);
  1. 乐观锁 当实体对象中,有字段使用JAP注解:@Version标注时,将为该字段启用乐观锁控制.
  2. 支持:整数型,日期类型和字符串类型(字符串类型使用UUID实现)的乐观锁功能.
  3. 默认的insert,update方法已经提供了乐观锁功能.另外,也提供了其他操作乐观锁的功能.
//具体可以查看api注释.
<T extends DBObject> int updateAndSet(T obj);
<T extends DBObject> int updateWithVersion(T obj);
  1. 查询 针对model的查询,只需要在实体类中,设置过值,即可.
  2. 也支持使用Example查询
user.useQuery().createCriteria().eq(User.Field.id, 1).and().eq(User.Field.displayName, "222");

如果使用Example查询,将忽略实体类中设值的查询(按主键查询除外).此查询对更新和删除同样有效.

/**
 * 根据主键查询
 * @param clz 实体类
 * @param keyParams 主键参数
 * @param <T> 泛型
 * @return 实体
 */
<T extends DBObject> T selectByPrimaryKeys(Class<T> clz, Object... keyParams);
/**
 * 查询总数
 * @param query 查询
 * @param <T> 泛型
 * @return 实体
 */
<T extends DBObject> long selectCount(T query);
/**
 * 查询一条记录,如果结果不唯一则抛出异常
 * @param query 查询条件
 * @return 查询结果
 */
<T extends DBObject> T selectOne(T query);
/**
 * 使用select ... for update 查询数据
 * @param query 查询条件
 * @param <T> 泛型
 * @return 实体
 */
<T extends DBObject> T selectOneForUpdate(T query);
/**
 * 查询列表
 * @param query 查询请求。
 * <ul>
 * <li>如果设置了Query条件,按query条件查询。 否则——</li>
 * <li>如果设置了主键值,按主键查询,否则——</li>
 * <li>按所有设置过值的字段作为条件查询。</li>
 * </ul>
 * @return 结果
 */
<T extends DBObject> List<T> selectList(T query);
/**
 * 使用select ... for update 查询数据
 * @param query 查询
 * @param <T> 泛型
 * @return 实体
 */
<T extends DBObject> List<T> selectListForUpdate(T query);
/**
 * 查询并分页
 * @param query 查询请求
 * @param start 起始记录,offset。从0开始。
 * @param limit 限制记录条数。如每页10条传入10。
 * @return 分页对象
 */
<T extends DBObject> Page<T> selectPage(T query, int start, int limit);
/**
 * 查询迭代结果.回调形式.
 * @param ormIt 迭代回调方法
 * @param query 查询
 * @param <T> 泛型
 */
<T extends DBObject> void selectIterator(OrmIterator<T> ormIt, T query);
/**
 * 查询限制条数和起始位置的迭代结果.回调形式.
 * @param ormIt 迭代回调方法
 * @param query 查询
 * @param start 起始数
 * @param limit 限制数
 * @param <T> 泛型
 */
<T extends DBObject> void selectIterator(OrmIterator<T> ormIt, T query, int start, int limit);
/**
 * stream lambda形式迭代结果.
 * @param ormStream 迭代回调方法
 * @param query 查询
 * @param <T> 泛型
 */
<T extends DBObject> void selectStream(OrmStream<T> ormStream, T query);

级联操作

  1. 级联关系配置 使用jpa注解,级联配置基本上和hibernate一样,唯一的区别在于,级联字段都需要使用Java字段名称,而不是数据库列名. 在jpa注解之外,还添加了额外的几个注解.
//此注解说明,需要抓取的级联对象的字段.主要是适用于,无需全部查询级联对象字段的值
@FetchDBField
  1. 级联对象插入 未提供单独的级联对象插入功能,可以使用普通的对象插入方法,保存级联对象.
  2. 级联对象修改 级联对象修改,需要在主对象完整的情况下.使用:
/**
 * 将对象插入数据库同时,也将指定级联字段的所有关联字段关联的对象统统插入相应的数据库
 * <p>
 * 关于关联字段更多信息,请参看 '@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 extends DBObject> T insertLinks(T obj, DBCascadeField... fields);
/**
 * 将对象的一个或者多个,多对多的关联信息,插入数据表
 * @param obj 对象
 * @param fields 正则表达式,描述了那种多对多关联字段将被执行该操作
 * @return 对象自身
 * @see javax.persistence.ManyToMany
 */
<T extends DBObject> T insertRelation(T obj, DBCascadeField... fields);
  1. 级联对象删除
/**
 * 将对象删除的同时,也将指定级联字段的所有关联字段关联的对象统统删除 <b style=color:red>注意:</b>
 * <p>
 * Java 对象的字段会被保留,这里的删除,将只会删除数据库中的记录
 * <p>
 * 关于关联字段更多信息,请参看 '@One' | '@Many' | '@ManyMany' 更多的描述
 * @param obj 对象
 * @param fields 指定字段,控制力度更细,至少一个或多个 描述了什么样的关联字段将被关注。如果为 null,则表示全部的关联字段都会被删除
 * @param <T> 泛型
 * @return 执行结果
 */
<T extends DBObject> int deleteCascade(T obj, DBCascadeField... fields);
/**
 * 仅删除对象所有的关联字段,并不包括对象本身。 <b style=color:red>注意:</b>
 * <p>
 * Java 对象的字段会被保留,这里的删除,将只会删除数据库中的记录
 * <p>
 * 关于关联字段更多信息,请参看 '@One' | '@Many' | '@ManyMany' 更多的描述
 * @param obj 数据对象
 * @param fields 字段名称,描述了什么样的关联字段将被关注。如果为 null,则表示全部的关联字段都会被删除
 * @return 被影响的记录行数
 * @see javax.persistence.OneToOne
 * @see javax.persistence.ManyToOne
 * @see javax.persistence.ManyToMany
 */
<T extends DBObject> int deleteLinks(T obj, DBCascadeField... fields);
/**
 * 多对多关联是通过一个中间表将两条数据表记录关联起来。
 * <p>
 * 而这个中间表可能还有其他的字段,比如描述关联的权重等
 * <p>
 * 这个操作可以让你一次删除某一个对象中多个多对多关联的数据
 * @param obj
 * @param fields 字段名称,描述了那种多对多关联字段将被执行该操作
 * @return 共有多少条数据被更新
 * @see javax.persistence.ManyToMany
 */
<T extends DBObject> int deleteRelation(T obj, DBCascadeField... fields);
  1. 级联对象查询
/**
 * 查找对象列表,并查询级联字段
 * @param query 查询
 * @param clz 实体类
 * @param fields 级联字段
 * @param <T> 泛型
 * @return 列表
 */
<T extends DBObject> List<T> fetchCascade(T query, Class<T> clz, DBCascadeField... fields);
/**
 * 查询对象,并返回所有的级联字段的值
 * @param obj 实体
 * @return 返回带级联字段值得对象
 */
<T extends DBObject> T fetchLinks(T obj);
/**
 * 查询单一对象
 * @param obj 实体类
 * @param fields 级联字段
 * @return 对象
 */
<T extends DBObject> 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
  1. in in中的变量值为Collection类型
#if(nicenames)
and nicename #in(nicenames)
#end
  1. 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
  1. pageIgnore 标签函数 pageIgnoreTag,用在翻页查询里,当查询用作统计总数的时候,会忽略标签体内容
  2. 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<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.queryDSLSelectPage(query,User.class, 2, 3);
  1. 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();
}
 
  1. 和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模板类型

七、daoMapper功能

框架支持类mybatis的mapper功能.

DBClient 提供了所有需要知道的API,但通过sqlid来访问sql有时候还是很麻烦,sorm支持Mapper,将sql文件映射到一个interface接口。接口的方法名与sql文件的sqlId一一对应。

接口必须实现DaoMapper接口,它提供内置的CRUID方法,如insert,update,merge,delete,unique,selectTemplate,executeTemplate,updateById等

DaoMapper 具备数据库常见的操作,接口只需要定义额外的方法与sqlId同名即可。

@OrmMapper
public interface UserDAOMapper extends DaoMapper<User> {
 @ExecuteTemplate(id = "db.user.selectUserByTemplateId")
 List<User> selectAllTemplateId(@OrmParam("id") int id);
}

如上select将会对应如下sql文件

#sql("db.user.selectUserByTemplateId")
 select * from wp_users where id=#p(id)
#end

你使用必须为JDK8,可以不必为参数提供名称,框架能自动对应。但必须保证java编译的时候开启-parameters选项。如果未开启编译参数,则可以使用@OrmParam注解()

@ExecuteTemplate
List<User> selectUserByTemplateId(@OrmParam("id") int id);

sorm的mapper方法会根据调用方法名字,返回值,以及参数映射到DBClient相应的查询接口,比如返回类型是List,意味着发起DBClient.selectTemplate 查询,如果返回是一个Map或者Pojo,则发起一次selectOneTemplate查询,如果返回定义为List,则表示查询实体,如果定义为List ,则对应的查询结果映射为Long

定义好接口后,可以通过DBClient.getMapper 来获取一个Dao真正的实现

UserDAOMapper dao = dbClient.getMapper(UserDAOMapper.class);

如果你使用Spring或者SpringBoot,可以参考Spring集成一章,了解如何自动注入Mapper

Mapper 对应的sql文件默认根据配置文件名来确定,可以通过@SqlResource 注解来指定Mapper的id前缀。比如

@OrmMapper
@SqlResource("db.user")
public interface UserCoreDao extends DaoMapper<User> {
 @ExecuteTemplate
 List<User> select(String name);
}
@OrmMapper
@SqlResource("db.userConsole")
public interface UserConsoleDao extends DaoMapper<User> {
 @ExecuteTemplate
 List<User> select(String name);
}

这样,这俩个mapper分别访问db.user.select 和 db.userConsole.select

7.1. 内置CRUD DaoMapper包含了内置的常用查询,如下

public interface DaoMapper<T extends DBObject> {
 SQLRelationalPath<T> queryDSLTable();
 JooqTable<?> jooqTable();
 /**
 * 合并记录
 * @param entity
 * @return
 */
 int merge(T entity);
 /**
 * 通用插入,插入一个实体对象到数据库,返回主键
 * @param entity
 */
 int insert(T entity);
 /**
 * 批量插入实体
 * @param list
 * @param fast 是否快速插入 如果是将不返还主键
 */
 int[] insertBatch(List<T> list, boolean fast);
 /**
 * 根据主键更新对象,对象set过才被更新
 * @param entity
 * @return
 */
 int updateById(T entity);
 int[] updateBatch(List<T> list);
 int delete(T entity);
 /**
 * 根据主键删除对象,如果对象是复合主键,传入对象本生即可
 * @param key
 * @return
 */
 int deleteById(Object... key);
 int[] deleteInBatch(Iterable<T> entities);
 void deleteAllInBatch();
 /**
 * 根据主键获取对象,如果对象不存在,则会抛出一个Runtime异常
 * @param keys
 * @return
 */
 T unique(Object... keys);
 /**
 * 根据主键获取对象,如果对象不存在,返回null
 * @param query
 * @return
 */
 T single(T query);
 /**
 * 根据主键获取对象,如果在事物中执行会添加数据库行级锁(select * from table where id = ? for
 * update),如果对象不存在,返回null
 * @param query
 * @return
 */
 T lock(T query);
 /**
 * 返回实体在数据库里的总数
 * @return
 */
 long count();
 boolean existsById(Object... id);
 boolean exists(T query);
 /**
 * 返回实体对应的所有数据库记录
 * @return
 */
 List<T> selectList(T query);
 /**
 * 返回实体对应的一个范围的记录
 * @param start
 * @param size
 * @return
 */
 Page<T> selectPage(T query, int start, int size);
 //////////////////// queryDSL /////////////
 /**
 * @param query
 * @return
 */
 List<T> queryDSLSelectList(AbstractSQLQuery<T, SQLQuery<T>> query);
 /**
 * @param query
 * @param start
 * @param limit
 * @return
 */
 Page<T> queryDSLSelectPage(AbstractSQLQuery<T, SQLQuery<T>> query, int start, int limit);
 /**
 * @param query
 * @param clz 返回值
 * @param <S>
 * @return
 */
 <S> S queryDSLSelectOne(AbstractSQLQuery<T, SQLQuery<T>> query, Class<S> clz);
 /**
 * @param query
 * @return
 */
 T queryDSLSelectOne(AbstractSQLQuery<T, SQLQuery<T>> query);
 /**
 * @param ormIt
 * @param query
 */
 void queryDSLSelectIterator(OrmIterator<T> ormIt, AbstractSQLQuery<T, SQLQuery<T>> query);
 /**
 * @param ormStream
 * @param query
 */
 void queryDSLSelectStream(OrmStream<T> ormStream, AbstractSQLQuery<T, SQLQuery<T>> query);
 //////////////// jooq ////////////////////
 /**
 * @param select
 * @return
 */
 T jooqSelectOne(Select<?> select);
 <S> S jooqSelectOne(Select<?> select, Class<S> clz);
 /**
 * @param select
 * @return
 */
 List<T> jooqSelectList(Select<?> select);
 /**
 * @param countSelect
 * @param pageSelect
 * @return
 */
 Page<T> jooqSelectPage(Select<?> countSelect, Select<?> pageSelect);
 /**
 * @param ormIt
 * @param select
 */
 void jooqSelectIterator(OrmIterator<T> ormIt, Select<?> select);
 /**
 * @param ormStream
 * @param select
 */
 void jooqSelectStream(OrmStream<T> ormStream, Select<?> select);
 ////////////////////// sql 模板执行 //////////////////////
 /**
 * 执行一个jdbc sql模板查询
 * @param sql
 * @param paras
 * @return
 */
 List<T> selectTemplate(String sql, Map<String, Object> paras);
 /**
 * 执行一个更新的jdbc sql
 * @param sql
 * @param paras
 * @return
 */
 int executeTemplate(String sql, Map<String, Object> paras);
 //////////>>>>>>>>>>>>>>其他<<<<<<<<<<<<<<<<<<<
 /**
 * @return
 */
 DBClient getDbClient();
 /**
 * @return
 */
 Class<T> getDomainClass();
}

7.2 sql语句 7.2.1 查询 @ExecuteSQL 执行原生sql语句查询,必须在方法上有@ExecuteSQL注解.

@ExecuteSQL(value = {"select * from wp_users"})
List<User> selectAll();

分页查询需要使用RowLimit限制对象查询个数

@ExecuteSQL(value = {"select * from wp_users"})
Page<User> selectSqlPage(RowLimit rl);

方法参数需要与"?" 一一对应

7.2.2 执行插入 对返回的主键需要使用 @SelectKey 该主键主要处理返回值的类型 以下是执行插入语句

@ExecuteSQL(value = {"insert into wp_users(login_name,activation_key) values(?,?)"})
@SelectKey(keyColumn = "id", resultType = long.class)
long insertUser(String loginName, String activeKey);

7.2.3 更新,删除和插入类似,唯一的区别是无返回值.

7.4 sql模板 @ExecuteTemplate 主要提供根据模板id执行模板语句以及直接执行模板语句.

@ExecuteTemplate(value = {"select * from wp_users where id =#p(id)"})
User selectSqlOneTemplate(@OrmParam("id") int id);

插入的返回值

@ExecuteTemplate(value = {"insert into wp_users(login_name,activation_key) values(#p(loginName),#p(activeKey))"}, type = DMLType.INSERT)
 @SelectKey(keyColumn = "id", resultType = Map.class)
 Map insertUserTemplate(@OrmParam("loginName") String loginName, @OrmParam("activeKey") String activeKey);

7.5 自动生成sql语句 @AutoSQL 标识该方法能自动生成sql, 使用该功能必须引入spring-data-commons依赖,框架使用spring-data-commons生成sql语句

@AutoSQL
List<User> findDistinctUserByIdOrderByLoginNameDesc(@Param("id") int id);

此处的@Param为spring-data-commons中的类.

7.6 额外功能 如果你觉得以上注解过于麻烦,还有一种用法,你可以继承DaoMapper接口,然后再实现该扩展接口即可.

public interface UserDAO extends DaoMapper<User> {
}
@Repository
public class UserDAOImpl extends DaoMapperImpl<User> implements UserDAO {
 public UserDAOImpl(@Autowired DBClient dbClient) {
 super(User.class, dbClient);
 }
}

八、其他功能

  1. 多数据源,DBClient中使用如下方法,即可切换数据源.
/**
 * 执行sql上下文,比如切换数据源
 * @param dataSource 数据源名称
 * @return DBClient
 */
DBClient useContext(String dataSource);
  1. 代码生成
/**
 * 根据表名生成对应的pojo类
 * @param pkg 包名,如 com.test
 * @param srcPath: 文件保存路径
 * @param config 配置生成的风格
 */
void genPojoCodes(String pkg, String srcPath, GenConfig config);
GenConfig genConfig = new GenConfig();
dbClient.genPojoCodes("target", System.getProperty("user.dir") + File.separator + "com/test/abc", genConfig);
  1. 分库分表功能 请使用第三方如:sharing-jdbc,mycat等整合.

相关推荐

团队管理“布阵术”:3招让你的团队战斗力爆表!

为何古代军队能够以一当十?为何现代企业有的团队高效似“特种部队”,有的却松散若“游击队”?**答案正隐匿于“布阵术”之中!**今时今日,让我们从古代兵法里萃取3个核心要义,助您塑造一支战斗力爆棚的...

知情人士回应字节大模型团队架构调整

【知情人士回应字节大模型团队架构调整】财联社2月21日电,针对原谷歌DeepMind副总裁吴永辉加入字节跳动后引发的团队调整问题,知情人士回应称:吴永辉博士主要负责AI基础研究探索工作,偏基础研究;A...

豆包大模型团队开源RLHF框架,训练吞吐量最高提升20倍

强化学习(RL)对大模型复杂推理能力提升有关键作用,但其复杂的计算流程对训练和部署也带来了巨大挑战。近日,字节跳动豆包大模型团队与香港大学联合提出HybridFlow。这是一个灵活高效的RL/RL...

创业团队如何设计股权架构及分配(创业团队如何设计股权架构及分配方案)

创业团队的股权架构设计,决定了公司在随后发展中呈现出的股权布局。如果最初的股权架构就存在先天不足,公司就很难顺利、稳定地成长起来。因此,创业之初,对股权设计应慎之又慎,避免留下巨大隐患和风险。两个人如...

消息称吴永辉入职后引发字节大模型团队架构大调整

2月21日,有消息称前谷歌大佬吴永辉加入字节跳动,并担任大模型团队Seed基础研究负责人后,引发了字节跳动大模型团队架构大调整。多名原本向朱文佳汇报的算法和技术负责人开始转向吴永辉汇报。简单来说,就是...

31页组织效能提升模型,经营管理团队搭建框架与权责定位

分享职场干货,提升能力!为职场精英打造个人知识体系,升职加薪!31页组织效能提升模型如何拿到分享的源文件:请您关注本头条号,然后私信本头条号“文米”2个字,按照操作流程,专人负责发送源文件给您。...

异形柱结构(异形柱结构技术规程)

下列关于混凝土异形柱结构设计的说法,其中何项正确?(A)混凝土异形柱框架结构可用于所有非抗震和抗震设防地区的一般居住建筑。(B)抗震设防烈度为6度时,对标准设防类(丙类)采用异形柱结构的建筑可不进行地...

职场干货:金字塔原理(金字塔原理实战篇)

金字塔原理的适用范围:金字塔原理适用于所有需要构建清晰逻辑框架的文章。第一篇:表达的逻辑。如何利用金字塔原理构建基本的金字塔结构受众(包括读者、听众、观众或学员)最容易理解的顺序:先了解主要的、抽象的...

底部剪力法(底部剪力法的基本原理)

某四层钢筋混凝土框架结构,计算简图如图1所示。抗震设防类别为丙类,抗震设防烈度为8度(0.2g),Ⅱ类场地,设计地震分组为第一组,第一自振周期T1=0.55s。一至四层的楼层侧向刚度依次为:K1=1...

结构等效重力荷载代表值(等效重力荷载系数)

某五层钢筋混凝土框架结构办公楼,房屋高度25.45m。抗震设防烈度8度,设防类别丙类,设计基本地震加速度0.2g,设计地震分组第二组,场地类别为Ⅱ类,混凝土强度等级C30。该结构平面和竖向均规则。假定...

体系结构已成昭告后世善莫大焉(体系构架是什么意思)

实践先行也理论已初步完成框架结构留余后人后世子孙俗话说前人栽树后人乘凉在夏商周大明大清民国共和前人栽树下吾之辈已完成结构体系又俗话说青出于蓝而胜于蓝各个时期任务不同吾辈探索框架结构体系经历有限肯定发展...

框架柱抗震构造要求(框架柱抗震设计)

某现浇钢筋混凝土框架-剪力墙结构高层办公楼,抗震设防烈度为8度(0.2g),场地类别为Ⅱ类,抗震等级:框架二级,剪力墙一级,混凝土强度等级:框架柱及剪力墙C50,框架梁及楼板C35,纵向钢筋及箍筋均采...

梁的刚度、挠度控制(钢梁挠度过大会引起什么原因)

某办公楼为现浇钢筋混凝土框架结构,r0=1.0,混凝土强度等级C35,纵向钢筋采用HRB400,箍筋采用HPB300。其二层(中间楼层)的局部平面图和次梁L-1的计算简图如图1~3(Z)所示,其中,K...

死要面子!有钱做大玻璃窗,却没有钱做“柱和梁”,不怕房塌吗?

活久见,有钱做2层落地大玻璃窗,却没有钱做“柱子和圈梁”,这样的农村自建房,安全吗?最近刷到个魔幻施工现场,如下图,这栋5开间的农村自建房,居然做了2个全景落地窗仔细观察,这2个落地窗还是飘窗,为了追...

不是承重墙,物业也不让拆?话说装修就一定要拆墙才行么

最近发现好多朋友装修时总想拆墙“爆改”空间,别以为只要避开承重墙就能随便砸!我家楼上邻居去年装修,拆了阳台矮墙想扩客厅,结果物业直接上门叫停。后来才知道,这种配重墙拆了会让阳台承重失衡,整栋楼都可能变...

取消回复欢迎 发表评论: