在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:sorm开源软件地址:https://gitee.com/parken/SormGit开源软件介绍:Small ORMsorms是一个全功能orm工具, 同时具有Hibernate与Mybatis的优点。该框架主要适合使用Spring,Spring boot的用户 主要功能特点介绍
快速预览
<!-- 引入jar包 --><dependency> <groupId>com.github.atshow</groupId> <artifactId>sorm</artifactId> <version>最新版本</version></dependency> 配置maven插件 <plugin> <groupId>com.github.atshow</groupId> <artifactId>sorm</artifactId> <version>最新版本</version> <executions> <execution> <goals> <goal>enhanceJavassist</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(); }
#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.*;import lombok.Data;@Data@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 }} 在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);
String sql = "select * from wp_users";List<User> list = dt.selectList(User.class, 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);
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);
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.getJooq().jooqSelectOne(query,User.class); 性能测试图测试 ##2018-12-15 16:17:51 更新
一、概述框架诞生初衷:Hibernate和Mybatis各走极端,在笔者实际使用中,Hibernate和Mybatis带来了极大的维护问题,导致大家有很多无意义的加班. Mybatis的问题引用大牛的话:Mybatis最大的问题不在于开发效率,而在维护效率上。其过于原生的数据库操作方式,难以避免项目维护过程中的巨大成本。当数据库字段变化带来的修改工作虽然可以集中到少数几个XML文件中,但是依然会分散在文件的各处,并且你无法依靠Java编译器帮助你发现这些修改是否有错漏。在一个复杂的使用Mybatis的项目中,变更数据库结构往往带来大量的CodeReview和测试工作,否则难以保证项目的稳定性。
参考:https://zhuanlan.zhihu.com/p/45044649 Hibernate的问题
额外说明
二、实体操作实体类继承为了实现实体类的动态更新,数据实体类需要继承:sf.core.DBObject public class XXX extends sf.core.DBObject DBObject类做了特殊设计:在json序列化时,不会序列化不相关的属性.对于的数据字段需要实现继承:sf.core.DBField接口的枚举 public enum Field implements sf.core.DBField{ XXX} 具体可以参考快速开发中的User类以及sorm-test工程.此处是为解析表结构做准备,对于数据字段的枚举描述,可以看到后面的Example查询,以及querydsl,jooq集成依赖这些字段. 实体类创建使用标准的JPA注解,框架中添加了额外的几个注解,用于补充JPA的表创建 实体类增强.在上面的例子中,还可以看到spring boot中的代码增强: new EntityEnhancerJavassist().enhance("db.domain"); 该代码主要是使用javassit对继承了DBObject的实体类做了静态代码增强.代码增强主要是对各个数据库字段的set方法做了增强,如下代码: @Column(length = 64)private String password;...public void setPassword(String password) { this.password = password;} 增强后代码变为: public void setPassword(String password) { if (this._recordUpdate) {//此处可实现当对象有set值后,即可更新或插入对应的值(无论是否为空). this.prepareUpdate(User.Field.password, password); } this.password = password;} 同时也提供基于ASM的实现使用maven构建时,可以配置Maven-Plugin,使其在编译完后自动扫描编译路径并执行增强操作。请使用: <plugin> <groupId>com.github.atshow</groupId> <artifactId>sorm</artifactId> <version>最新版本号</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());
dao.insert(user);//使用该方法插入后,主键值将自动被写入user对象中.//批量插入List<User> modelList = new ArrayList<>();....dao.batchInsert(modelList); 除上面的用法,也提供了快速单一插入和快速批量插入的方法(快速插入都不返回主键) dao.insertFast(user);//批量插入List<User> modelList = new ArrayList<>();....dao.batchInsertFast(modelList);
//更新对象,如果有查询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);
//删除对象.如果有查询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);
//具体可以查看api注释.<T extends DBObject> int updateAndSet(T obj);<T extends DBObject> int updateWithVersion(T obj);
user.useQuery().createCriteria().eq(User.Field.id, 1).and().eq(User.Field.displayName, "222"); 如果使用Example查询,将忽略实体类中设值的查询(按主键查询除外).此查询对更新和删除同样有效.更详细的方法注释可以查看DBMethod类. //根据主键查询<T extends DBObject> T selectByPrimaryKeys(Class<T> clz, Object... keyParams);//查询总数<T extends DBObject> long selectCount(T query);//查询一条记录,如果结果不唯一则抛出异常<T extends DBObject> T selectOne(T query);//使用select ... for update 查询数据<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 实体 *///使用select ... for update 查询数据<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);//查询迭代结果.回调形式.<T extends DBObject> void selectIterator(Consumer<Iterable<T>> ormIt, T query);/** * 查询限制条数和起始位置的迭代结果.回调形式. * @param ormIt 迭代回调方法 * @param query 查询 * @param start 起始数 * @param limit 限制数 * @param <T> 泛型 *///查询限制条数和起始位置的迭代结果.回调形式.<T extends DBObject> void selectIterator(Consumer<Iterable<T>> ormIt, T query, int start, int limit);//stream lambda形式迭代结果.<T extends DBObject> void selectStream(Consumer<Stream<T>> ormStream, T query); 级联操作
//此注解说明,需要抓取的级联对象的字段.主要是适用于,无需全部查询级联对象字段的值@FetchDBField
/** * 将对象插入数据库同时,也将指定级联字段的所有关联字段关联的对象统统插入相应的数据库 * <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);
/** * 将对象删除的同时,也将指定级联字段的所有关联字段关联的对象统统删除 <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);
/** * 查找对象列表,并查询级联字段 * @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); Example 查询该查询和Mybatis-generator中的Example类似,主要是参考tk.mybatis.mapper 实现.简单使用 User user=new User();user.useQuery().createCriteria().eq(User.Field.id, 1).and().eq(User.Field.displayName, "222");List<User> list = dt.selectList(user); Example支持查询,修改,删除功能.注意:为了提高Example的灵活性,Example中添加了and(),or(),leftP(),rightP() 分别对应:and,or,左括号,右括号等. 三、sql操作此大类方法主要是为了兼容普通jdbc操作,使与JdbcTemplate使用一致.
//此注解实现:主要解决子对象的创建问题.@SmallResults({ @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; @Tail private Map<String,Object> map; ..... 省略get set public class MetaResult { private Long userId; private String icon; ..... 省略get set 四、模板操作模板主要使用enjoy模板实现,基本功能和jfinal中的enjoy sql模板一致,框架只支持Map类型参数传入.文档地址: https://jfinal.com/doc #sql("user_cols") id,login_name,nicename#end#sql("user_condition") #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#sql("queryUserByName")select #use("user_cols") from wp_users #use("user_condition")#end//java调用Map<String, Object> params = new HashMap<>();params.put("id", 1);List<Object> nicenames = Arrays.asList("1", "aa");params.put("nicenames", nicenames);List<User> list = dt.selectListTemplate(User.class, "queryUserByName", params); 模板支持跨数据库 #sql("queryUserByName.mysql") ...#end// 对于mysql数据库框架会查找模板id为 mysql.queryUserByName 的模板内容,如果未找到,则会使用默认的queryUserByName//支持:oracle, sqlserver, db2, derby, postgresql, mysql, mariadb, hsqldb, access, gbase, sqlite, mongo, h2, cubrid, firebird// 注意都为小写.dt.selectListTemplate(User.class, "queryUserByName", params) 模板指令
id=#p(id)
#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
#if(nicenames)and nicename #in(nicenames)#end not in 可以使用组合方式实现 #if(nicenames)and nicename not #in(nicenames)#end
#sql("queryUserByNamePage")select #page("*") 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
#pageIgnore("order by id") #pageIgnoreTag() order by id#end
#sql("user_cols") id,login_name,nicename#end#sql("queryUserByName")select #use("user_cols") from wp_users #use("user_condition")#end
#namespace("db.user") #sql("selectUserByTemplateId") select * from wp_users where id=#p(id) #end#end 五、整合QueryDSL,jOOQ,mybatis-dynmic-sql整合QueryDSL和JOOQ主要是为了提供类型安全的查询语句的生成,避免代码里出现过多的硬编码sql语句,导致维护噩梦.框架主要以使用固定表为主.
<dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql</artifactId></dependency>
全部评论
专题导读
上一篇:kvproxy: 一个KV类型数据库的代理框架。可以通过编写扩展(so动态库)的方式增加对指 ...发布时间:2022-03-24下一篇:polymeric: 提供跨微服务数据聚合优雅高效的实现发布时间:2022-03-24热门推荐
热门话题
阅读排行榜
|
请发表评论