• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

dbm: a simple Java ORM Framework, based on spring-jdbc.

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

dbm

开源软件地址:

https://gitee.com/pistols/dbm

开源软件介绍:

dbm


基于spring jdbc实现的轻量级orm

项目github地址: dbm

交流群: 604158262

目录

特色

  • 基本的实体增删改查(单表)不需要生成样板代码和sql文件。

  • 返回结果不需要手动映射,会根据字段名称自动映射。

  • 支持sql语句和接口绑定风格的DAO,但sql不是写在丑陋的xml里,而是直接写在sql文件里,这样用eclipse或者相关支持sql的编辑器打开时,就可以语法高亮,更容易阅读。

  • 支持sql脚本修改后重新加载

  • 内置支持分页查询。

  • 接口支持批量插入

  • 使用Java8新增的编译特性,不需要使用类似@Param 的注解标注参数,当然你可以显式使用注解标注参数。

  • Repository接口(用注解@DbmRepository标注了的接口)支持默认方法

  • 支持多数据源绑定,可以为每个Repository接口指定具体的数据源

  • 支持不同的数据库绑定,Repository接口会根据当前绑定的数据源自动绑定加载对应数据库后缀的sql文件

  • 提供充血模型支持

  • 支持json映射,直接把数据库的json或者varchar类型(存储内容为json数据)的列映射为Java对象

  • 支持敏感字段映射

示例项目

单独使用dbm的示例项目boot-dbm-sample

要求

JDK 1.8+spring 4.0+

maven

当前snapshot版本:4.7.4-SNAPSHOT

若使用snapshot版本,请添加snapshotRepository仓储:

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

添加依赖:

<dependency>    <groupId>org.onetwo4j</groupId>    <artifactId>onetwo-dbm</artifactId>    <version>4.7.4-SNAPSHOT</version></dependency>

spring的依赖请自行添加。

一行代码启用

在已配置好数据源的前提下,只需要在spring配置类(即有@Configuration注解的类)上加上注解@EnableDbm即可。

  	@EnableDbm	@Configuration	public class SpringContextConfig {	}      

实体映射

@Entity   @Table(name="TEST_USER_AUTOID")   public class UserAutoidEntity {	@Id	@GeneratedValue(strategy=GenerationType.IDENTITY) 	@Column(name="ID")	protected Long id;	@Length(min=1, max=50)	protected String userName;	@Length(min=0, max=50)	@Email	protected String email;	protected String mobile;	protected UserStatus status;	//省略getter和setter}   

注意这里用到了一些jpa的注解,含义和jpa一致:

  • @Entity,表示这是一个映射到数据库表的实体
  • @Table,表示这个实体映射的表
  • @Id,表示这是一个主键字段
  • @GeneratedValue(strategy=GenerationType.IDENTITY),表示这个主键的值用数据库自增的方式生成,dbm目前只支持IDENTITY和SEQUENCE两种方式
  • @Column,表示映射到表的字段,一般用在java的字段名和表的字段名不对应的时候

java的字段名使用驼峰的命名风格,而数据库使用下划线的风格,dbm会自动做转换
注意dbm并没有实现jpa规范,只是借用了几个jpa的注解,纯属只是为了方便。。。后来为了证明我也不是真的很懒,也写了和@Entity、@Table、@Column对应的注解,分别是:@DbmEntity(@Entity和@Table合一),@DbmColumn。。。

  • 注意:为了保持简单和轻量级,dbm的实体映射只支持单表,不支持多表级联映射。复杂的查询和映射请使用DbmRepository接口

id策略

dbm支持jpa的GenerationType的id策略,此外还提供了通过@DbmIdGenerator自定义的策略:

  • GenerationType.IDENTITY
    使用数据库本身的自增策略
  • GenerationType.SEQUENCE
    使用数据库的序列策略(只支持oracle)
  • GenerationType.TABLE
    使用自定义的数据库表管理序列
  • GenerationType.AUTO
    目前的实现是:如果是mysql,则等同于GenerationType.IDENTITY,如果是oracle,则等同于GenerationType.SEQUENCE
  • DbmIdGenerator
    dbm提供id生成注解,可通过配置 generatorClass 属性,配置自定义的id实现类,实现类必须实现CustomIdGenerator接口。dbm首先会通过尝试在spring context查找generatorClass类型的bean,如果找不到则通过反射创建实例。

详细使用

GenerationType.IDENTITY

@Entity@Table(name="t_user")public class UserEntity implements Serializable {	@Id	@GeneratedValue(strategy=GenerationType.IDENTITY) 	protected Long id;}

GenerationType.TABLE

@Entity@Table(name="t_user")public class UserEntity implements Serializable {	@Id	@GeneratedValue(strategy = GenerationType.TABLE, generator="tableIdGenerator")  	@TableGenerator(name = "tableIdGenerator",  	    table="gen_ids",  	    pkColumnName="gen_name",  	    valueColumnName="gen_value",  	    pkColumnValue="seq_test_user",  	    allocationSize=50	)	protected Long id;}

GenerationType.SEQUENCE

@Entity@Table(name="t_user")public class UserEntity implements Serializable {	@Id	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seqGenerator") 	@SequenceGenerator(name="seqGenerator", sequenceName="SEQ_TEST_USER")	protected Long id;}

DbmIdGenerator

比如使用了dbm集成的snowflake策略,下面的配置使用了默认配置的snowflake,如果需要配置不同的datacenter和machine,建议自己实现CustomIdGenerator接口。

@Entity@Table(name="t_user")public class UserEntity implements Serializable {	@Id  	@GeneratedValue(strategy = GenerationType.AUTO, generator="snowflake") 	@DbmIdGenerator(name="snowflake", generatorClass=SnowflakeGenerator.class)	protected Long id;}

复合主键映射

jpa支持三种复合主键映射策略,dbm目前只支持一种: @IdClass 映射。映射方法如下:假设有一个表有两个主键:id1,id2。实体的Java代码如下:

@Data@Entity@Table(name="composite_table")@IdClass(CompositeId.class)public class CompositeEntity {	@Id  	Long id1;	@Id	Long id2;	@Transient	CompositeId id;	public CompositeId getId() {		return new CompositeId(id1, id2);	}		public void setId(CompositeId id) {		this.id1 = id.getId1();		this.id2= id.getId2();	}		//....其它属性	@Data	public static class CompositeId implements Serializable {		Long id1;		Long id2;	}}

解释:

  • 把需要映射为主键的实体属性都用 @Id 注解标注
  • 另外创建一个复合主键的Pojo类CompositeId,属性为实体需要映射为主键的属性,名称类型一一对应,并实现 java.io.Serializable 接口
  • 在实体类里用 @IdClass 注解标注为复合主键类为 CompositedId 类
  • 实体的CompositeId属性不是必须的,只是为了更方便使用组合id,而且无需持久化,所以如果写的话,需要用 @Transient 注解标注

复合主键实体的查找方法为:

CompositedId cid = new CompositedId(1, 1);CompositeEntity entity = baseEntityManager.load(CompositeEntity.class, cid);int deleteCount = baseEntityManager.removeById(CompositeEntity.class, entity.getId());

枚举处理

枚举映射

dbm支持jpa的@Enumerated枚举映射注解,使用方法和jpa一样,默认为EnumType.ORDINAL int值类型映射,可以通过注解属性指定为EnumType.STRING名称映射。

但是,当枚举为EnumType.ORDINAL映射的时候,ordinal的值是从0开始根据定义时的先后顺序决定,这使得我们开发的时候很不方便,比如我有一个枚举类型,是需要映射为int类型,但是值并不是从0开始的,这时候就相当的尴尬,因为你既不能用默认为EnumType.ORDINAL,也不能用EnumType.STRING。

所以dbm还另外增加了自定义的int值映射接口DbmEnumValueMapping,只要枚举类型实现了这个接口,就可以自定义返回实际的映射值,比如:

@Entity@Table(name="TEST_USER")public class UserEntity {	@Id	Long id;	@Enumerated(EnumType.ORDINAL)	UserGenders gender;	public static enum UserGenders {		FEMALE("女性"),		MALE("男性");				final private String label;		private UserGenders(String label) {			this.label = label;		}		public String getLabel() {			return label;		}	}}

如果按照jpa的做法,枚举类型映射为@Enumerated(EnumType.ORDINAL)后,用户实体的gender属性对应的数据库列只能是0(FEMALE)和1(MALE)。在dbm里,你可以通过实现DbmEnumValueMapping接口,返回自定义的映射值,比如10(FEMALE)和11(MALE)。

@Entity@Table(name="TEST_USER")public class UserEntity {	@Id	Long id;	@Enumerated(EnumType.ORDINAL)	UserGenders gender;	public static enum UserGenders implements DbmEnumValueMapping {		FEMALE("女性", 10),		MALE("男性", 11);				final private String label;		final private int value;		private UserGenders(String label, int value) {			this.label = label;			this.value = value;		}		public String getLabel() {			return label;		}		@Override		public int getMappingValue() {			return value;		}			}}

枚举属性查询时的处理

  • 如果枚举实现了 DbmEnumValueMapping 接口,则取DbmEnumValueMapping#getMappingValue()方法所得的值
  • 通过Querys 和 BaseEntityManager 的api查询时,一般直接取枚举的name()方法所得的值
  • 如果是@DbmRepository 接口,并且用@Param注解指定了enumType属性,则根据配置的取相应的值,但是DbmEnumValueMapping接口优先级更高

json映射

有时候,我们需要在数据库的某个字段里存储json格式的数据,又想在获取到数据后转为java对象使用,这时你可以使用 @DbmJsonField 注解,这个注解会在保存实体的时候把对象转化为json字符串,然后在取出数据的时候自动把字符串转化为对象。示例:

class SimpleEntity {	@DbmJsonField	private ExtInfo extInfo;		public static class ExtInfo {		String address;		List<String> phones;	}}

如果该字段是泛型,需要保存类型信息,可以设置storeTyping属性为true

class SimpleEntity {	@DbmJsonField(storeTyping=true)	private Map<String, ConfigData> configData;		public static class ExtInfo {		String address;		List<String> phones;	}}

需要添加依赖:

    <dependency>      <groupId>org.onetwo4j</groupId>      <artifactId>onetwo-jackson</artifactId>    </dependency>

敏感字段映射

加解密映射

对于一些不适宜明文存储的字段信息,比如api密钥,存储的时候自动加密,获取的时候自动解密,此时可以使用@DbmEncryptField 注解。

@Entity@Table(name="TEST_MERCHANT")public class MerchantEntity implements Serializable {		@Id	@GeneratedValue(strategy=GenerationType.IDENTITY) 	@Column(name="ID")	protected Long id;		@DbmEncryptField	protected String apikey;}

在@DbmRepository 使用这个功能时,可以在插入的参数后面加上后缀函数:

/***** * @name: batchInsert * 批量插入     */    insert     into        test_merchant        (id, apikey)     values        (:id, :apikey?encrypt)

注意

  • dbm的敏感字段加密功能依赖jasypt

  • 你可以通过下面属性配置jasypt的StandardPBEStringEncryptor

    dbm:     encrypt:         algorithm: PBEWithMD5AndTripleDES #默认加密算法        password: test #密钥

脱敏映射

对于另一些字段,我们可能并不需要加解密,而只是在存储或者获取的时候,按照一定的规则脱敏。比如手机号码取出的时候自动对后面四位打上星号,或者邮件地址只显示第一个字符和@后面的字符,则可以使用 @DbmSensitiveField 注解进行脱敏映射。

@Entity@Table(name="TEST_USER")public class UserEntity implements Serializable {		@Id	@GeneratedValue(strategy=GenerationType.IDENTITY) 	@Column(name="ID")	private Long id;		private String mobile;	        @DbmBindValueToField(name="mobile") //查询实体时,此字段的值来自mobile字段        @Transient //此字段无需保存到数据库	@DbmSensitiveField(leftPlainTextSize=7, on=SensitiveOns.SELECT)	// 保留手机号码只显示左边7位,如13612345678,取出脱敏后mobile的值为:1361234****	private String mobileUnsensitive;		@DbmSensitiveField(leftPlainTextSize=1, sensitiveIndexOf="@",  on=SensitiveOns.SELECT)	// 邮件地址左边保留一个长度的字符,@后面的字符都保留,其余用星号代替,如[email protected],取出脱敏后为:t***@gmail.com	private String email;}

解释

DbmSensitiveField 属性解释如下:

  • on: 表示进行脱敏的时机,有两个选择:STORE(保存到数据库的时候),SELECT(从数据库获取出来转换为java对象的时候)
  • leftPlainTextSize: 脱敏时需要左边保持明文的字符长度
  • rightPlainTextSize: 脱敏时需要右边保持明文的字符长度
  • sensitiveIndexOf: 当不想整个字段进行脱敏的时候,此属性表示某个指定的字符索引作为脱敏的结束索引。比如邮件脱敏,@字符后面的保留时,此属性值可以写为"@"
  • replacementString: 替换敏感数据的字符串,默认为星号

注意

此功能从 4.7.4 版本开始支持

字段绑定

@DbmBindValueToField 注解可以帮某个字段的值绑定到另一个字段,绑定后,实体查询时,此字段的值将会取自绑定的值。例子可以参考 脱敏映射

注意

此功能从 4.7.4 版本开始支持

其它特有的映射

@DbmField注解

@DbmField 注解可自定义一个值转换器,用于从数据库表获取的字段值转换为Java对象的属性值,和把Java对象的属性值转换为数据库表的字段值。
@DbmJsonField 注解实际上是包装了@DbmField注解实现的。

BaseEntityManager接口和QueryDSL

大多数数据库操作都可以通过BaseEntityManager接口来完成。
BaseEntityManager可直接注入。

先来个简单的使用例子:

		@Resource	private BaseEntityManager entityManager;	@Test	public void testSample(){		UserAutoidEntity user = new UserAutoidEntity();		user.setUserName("dbm");		user.setMobile("1333333333");		user.setEmail("[email protected]");		user.setStatus(UserStatus.NORMAL);				//save		Long userId = entityManager.save(user).getId();		assertThat(userId, notNullValue());				//update		String newMobile = "13555555555";		user.setMobile(newMobile);		entityManager.update(user);				//fetch by id		user = entityManager.findById(UserEntity.class, userId); 		assertThat(user.getMobile(), is(newMobile));				//通过实体属性查找,下面的调用相当于sql条件: where mobile='13555555555' and status IN ('NORMAL', 'DELETE') and age>18		user = entityManager.findOne(UserAutoidEntity.class, 										"mobile", newMobile,										"status:in", Arrays.asList(UserStatus.NORMAL, UserStatus.DELETE),										"age:>", 18);		assertThat(user.getId(), is(userId));		//下面的调用相当于sql条件: where registerTime>=:date1 and registerTime<:date2		entityManager.findList(UserEntity.class, "registerTime:date in", new Object[]{date1, date2})					}

BaseEntityManager对象的find开头的接口,可变参数一般都是按键值对传入,相当于一个Map,键是实体对应的属性(+冒号+操作符,可选,不加默认就是=),值是对应属性的条件值:

entityManager.findOne(entityClass, propertyName1, value1, propertyName2, value2......);   entityManager.findList(entityClass, propertyName1, value1, propertyName2, value2......);

key,value形式的参数最终会被and操作符连接起来。

其中属性名和值都可以传入数组或者List类型的参数,这些多值参数最终会被or操作符连接起来,比如:

  • 属性名参数传入一个数组:
entityManager.findList(entityClass, new String[]{propertyName1, propertyName2}, value1, propertyName3, value3);

最终生成的sql语句大概是:

select t.* from table t where (t.property_name1=:value1 or t.property_name2=:value1) and t.property_name3=:value3
  • 属性值参数传入一个数组:
entityManager.findList(entityClass, propertyName1, new Object[]{value1, value2}, propertyName3, value3);

最终生成的sql语句大概是:

select t.* from table t where (t.property_name1=:value1 or t.property_name1=:value2) and t.property_name3=:value3
  • find 风格的api会对一些特殊参数做特殊的处理,比如 K.IF_NULL 属性是告诉dbm当查询值查找的属性对应的值为null或者空时,该如何处理,IfNull.Ignore表示忽略这个条件。 **比如:
entityManager.findList(entityClass, propertyName1, new Object[]{value1, value2}, propertyName3, value3, K.IF_NULL, IfNull.Ignore);

那么,当value3(或者任何一个属性对应的值)为nul时,最终生成的sql语句大概是:

select t.* from table t where (t.property_name1=:value1 or t.property_name1=:value2) 

property_name3条件被忽略了。

操作符

BaseEntityManager的属性查询支持如下操作符:
=, >, <, !=, in, not in, date in, is null, like, not like

Query DSL API

dbm还提供了一个专门用于构建查询的dsl api

//使用 querys dsl apiUserAutoidEntity queryUser = Querys.from(entityManager, UserAutoidEntity.class)									.where()										.field("mobile").is(newMobile)										.field("status").is(UserStatus.NORMAL)									.end()									.toQuery()									.one();assertThat(queryUser, is(user));

注意:4.7.3后,query dsl api 已集成到 BaseEntityManager 接口,可以通过 BaseEntityManager 直接创建查询:

public Optional<User> findBy(String month, Long userId) 
                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap