Skip to content

pengweizhong/dynamic-sql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

项目说明

动态SQL基于JDBC,旨在简化开发人员在数据库访问层面的工作。基于此背景打造的低耦合的最小化依赖。
同时解决贴合项目中的自定义映射、字段解析、SQL拦截、自定义ID自增功能,可完美结合各个项目中的底层通用包使用而不依赖spring环境。
还可以根据不同的条件动态生成语句,更便捷的对数据库单表的增删改查,使得开发人员只需注重业务,提高代码的灵活性和可维护性。

主要特性:

  1. 单表动态增删改查
  2. 不依赖其他框架环境,可单独启动
  3. 支持多数据源
  4. 支持事务(目前仅spring环境)
  5. 支持直接执行自定义SQL语句
  6. 支持Mysql,Oracle
  7. 提供了spring-boot-starter用于快速启动

资源引入

Maven

	<-- 单体项目 -->
	<dependency>
	    <groupId>com.pengwz</groupId>
	    <artifactId>dynamic-sql</artifactId>
	    <version>2.1.7</version>
	</dependency>
	
	<-- SpringBoot项目,已集成 Dynamic-SQL -->
	<dependency>
	    <groupId>com.pengwz</groupId>
	    <artifactId>dynamic-sql-spring-boot-starter</artifactId>
	    <version>2.1.7</version>
	</dependency>

Gradle

implementation group: 'com.pengwz', name: 'dynamic-sql', version: '2.1.7'

快速开始

以下案例均可以在test/java/com.pengwz.demo测试包下找到。

  1. 配置数据源
    1.1 使用JDBC创建数据源
    1.2 使用Druid创建数据源
  2. 配置实体类
  3. 新增
    3.1 新增单条记录
    3.2 有选择的新增
    3.3 新增多条记录
    3.4 新增或更新
  4. 查询
    4.1 使用简单函数查询总数量
    4.2 根据主键查询
    4.3 根据条件查询
  5. 更新
    5.1 根据条件更新
    5.2 根据主键更新
    5.3 有选择的更新
    5.4 批量更新
  6. 删除
    6.1 根据条件删除
    6.2 根据主键删除

准备些测试数据(mysql 8.x版本及以上,否则会执行报错,若使用的mysql8以下的版本,则删除新语法即可) 此处简单的准备了两个表,一张用户表,一张角色表。

drop table if exists `t_user`;
CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `account_no` varchar(50) DEFAULT NULL COMMENT '账号',
  `username` varchar(50) DEFAULT NULL COMMENT '用户名',
  `password` varchar(50) DEFAULT NULL COMMENT '密码',
    `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
    `birthday` datetime DEFAULT NULL COMMENT '生日',
    `desc` varchar(50) DEFAULT NULL COMMENT '邮箱',
    `is_delete` tinyint(1)  DEFAULT NULL COMMENT '是否删除 true 已删除 false 未删除',
  `create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `t_user_UN` (`account_no`)
) ENGINE=InnoDB COMMENT ='用户表';

drop table if exists `t_user_role`;
CREATE TABLE `t_user_role` (
  `uid` varchar(50) NOT NULL  COMMENT 'UUID主键',
  `username` varchar(50) DEFAULT NULL COMMENT '用户名',
  `role` varchar(50) DEFAULT NULL COMMENT '角色',
  `create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT ='用户和角色表';

1. 配置数据源

1.1 使用JDBC创建数据源

创建一个实体类,实现DataSourceConfig接口,重写getDataSource()方法。此处命名为DatabaseConfig

public class DatabaseConfig implements DataSourceConfig {
    @Override
    public DataSource getDataSource() {
        MysqlDataSource ds = new MysqlDataSource();
        ds.setUrl("jdbc:mysql://127.0.0.1:3306/dynamic?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=GMT%2B8&characterEncoding=utf-8");
        ds.setUser("root");
        ds.setPassword("pengwz");
        return ds;
    }
}

DataSourceConfig提供了两个方法:

    DataSource getDataSource();

    default boolean defaultDataSource() {
        return false;
    }
  • getDataSource是用于获取数据源连接,可以使用jdbc自带的连接池,如MysqlDataSource
  • defaultDataSource 是在多数据源情况下,用于指定默认数据源,默认值false。该条件目前仅使用spring环境,其他环境目前即使重写该值,也是没有用的。(单体项目也许有解决办法,但是目前没想到。。。)

1.2 使用druid创建数据源

首先,在pom中引入druid的依赖:

	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>druid</artifactId>
		<version>1.2.4</version>
	</dependency>

然后再配置类中构建连接参数:

public class DatabaseConfig implements DataSourceConfig {
    @Override
    public DataSource getDataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl("jdbc:mysql://127.0.0.1:3306/dynamic?useOldAliasMetadataBehavior=true&useUnicode=true&rewriteBatchedStatements=true&serverTimezone=GMT%2B8&characterEncoding=utf-8");
        ds.setUsername("root");
        ds.setPassword("pengwz");
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setInitialSize(3);
        ds.setMaxActive(10);
        ds.setMinIdle(5);
        ds.setValidationQuery("select 1");
        ds.setTestOnBorrow(true);
        ds.setTestOnReturn(false);
        ds.setUseUnfairLock(true);
        ds.setTestWhileIdle(true);
        ds.setMinEvictableIdleTimeMillis(10 * 60 * 1000L);
        ds.setTimeBetweenEvictionRunsMillis(5 * 60 * 1000L);
        return ds;
    }
}

2. 配置实体类

执行完上面的SQL后,随后在项目中分别创建对应的实体类

// 在spring环境下设置好默认数据源后,就无需在此处声明
@Table(value = "t_user", dataSourceClass = DatabaseConfig.class)
public class UserEntity {
	@Id
	@GeneratedValue
	private Long id;
	private String username;
	private String password;
	private LocalDateTime createDate;
	@Column("update_date")
	private LocalDateTime updateDate;
	/** getter and setter **/
	/** toString() **/
}

@Table(value = "t_user", dataSourceClass = DatabaseConfig.class)
public class UserRoleEntity {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String role;
    private LocalDateTime createDate;
    @Column("update_date")
    private LocalDateTime updateDate;
	/** getter and setter **/
	/** toString() **/
}

@Table注解用于维护数据表和实体类关系,该注解提供了两个属性:

  • value:表示对应数据库表名,必须提供该值
  • dataSourceClass:表示该表所属数据源,非spring环境必须提供

@Id注解用于表示那个属性对应表主键,在实体类中标识该主键后,即可用于后续根据主键查询、更新等操作,该注解非强制添加,但建议使用
@GeneratedValue注解用于在新增数据后,返回主键值。该注解非强制添加,但建议使用
@Column注解用于维护表列和实体类属性的关系,对于该情况下有下列处理办法:

  • 当实体类属性标注了 @Column注解,则以注解内value匹配表列;
  • 若没有 @Column注解,则以实体类的属性,根据驼峰命名规则匹配表列,如:实体类属性createDate匹配表列create_date

dymamic-sql 对实体类类型映射进行了加强,比如日期类在实体类中可以使用java.util.Date接收,或者使用java.sql.Datejava.time.LocalDateTime接收等等。 实体类属性必须使用引用类型,因为基本类型在任何时候都不等于null

3. 新增

3.1 新增单条记录

t_user表新增一条数据

    @Test
    public void insert() {
        UserEntity entity = new UserEntity();
        entity.setUsername("tom");
        entity.setCreateDate(LocalDateTime.now());
        entity.setUpdateDate(LocalDateTime.now());
        entity.setPassword("password");
        Integer insert = BraveSql.build(UserEntity.class).insert(entity);
        //1
        System.out.println(insert);
    }

执行结果:
gkU3LT.png
也可以对UserEntity原始对象进行输出,其自增ID已经映射到该实体类中,方便在业务中进行其他操作。此处便不再打印输出结果。

3.2 有选择的新增

    @Test
    public void insertActive() {
        UserEntity entity = new UserEntity();
        entity.setUsername("jerry");
        entity.setPassword("password");
        Integer insert = BraveSql.build(UserEntity.class).insertActive(entity);
        //1
        System.out.println(insert);
    }

执行结果:
gkaPk4.png BraveSql.build(UserEntity.class).insert(entity)BraveSql.build(UserEntity.class).insertActive(entity)区别在于insert插入值时,如果该属性为null,则插入null; insertActive插入值时,如果该属性为null,则使用数据库默认值。

3.3 新增多条记录

批量插入10条记录

    @Test
    public void batchInsert() {
        List<UserEntity> userEntities = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            UserEntity entity = new UserEntity();
            entity.setUsername("list_"+i);
            entity.setPassword("password_"+i);
            entity.setCreateDate(LocalDateTime.now());
            entity.setUpdateDate(LocalDateTime.now());
            userEntities.add(entity);
        }
        Integer insert  = BraveSql.build(UserEntity.class).batchInsert(userEntities);
        //10
        System.out.println(insert);
    }

执行结果:
gkdkVS.png

3.4 新增或更新

新增或更新,主要场景用于数据新增时,不确定数据是否存在,可以使用insertOrUpdate方法,避免插入前还需要查询数据是否存在。 判断原始数据是否存在是以主键约束、唯一约束等等来决定数据是插入还是新增。
如:插入Id为1的数据(Id为1的数据表中已经存在)

    @Test
    public void insertOrUpdate() {
        UserEntity entity = new UserEntity();
        entity.setId(1L);
        entity.setUsername("tom_update");
        entity.setCreateDate(LocalDateTime.now());
        entity.setUpdateDate(LocalDateTime.now());
        entity.setPassword("password_update");
        Integer insert = BraveSql.build(UserEntity.class).insertOrUpdate(entity);
        System.out.println(insert);
    }

执行结果:
gkw3OP.png

4. 查询

4.1 使用简单函数查询

    @Test
    public void test1() {
        //查询总数量
        Integer count = BraveSql.build(UserEntity.class).selectCount();
        System.out.println(count);
        //对所有ID求和
        BigDecimal sum = BraveSql.build(UserEntity.class).selectSum(UserEntity::getId);
        System.out.println(sum);
        // ......
    }

4.2 根据主键查询

    @Test
    public void test2() {
        UserEntity userEntity = BraveSql.build(UserEntity.class).selectByPrimaryKey(1);
        System.out.println(userEntity);
    }

4.3 根据条件查询

4.3.1 一般条件查询

    @Test
    public void test3() {
        //查询用户名=jerry的数据
        DynamicSql<UserEntity> dynamicSql = DynamicSql.createDynamicSql();
        //可以直接使用字段名进行查询,但是i推荐使用表达式
        //dynamicSql.andEqualTo("username","jerry");
        dynamicSql.andEqualTo(UserEntity::getUsername, "jerry");
        List<UserEntity> entities = BraveSql.build(dynamicSql, UserEntity.class).select();
        System.out.println(entities);
    }

4.3.2 分页查询

    @Test
    public void test4() {
        //按5个为一组分页,取第一页
        PageInfo<UserEntity> pageInfo = BraveSql.build(UserEntity.class).selectPageInfo(1, 5);
        Assert.assertEquals(1, (int) pageInfo.getPageIndex());
        Assert.assertEquals(5, (int) pageInfo.getPageSize());
        Assert.assertEquals(5, pageInfo.getResultList().size());
    }

4.3.3 复杂查询(创建带括号的查询)

    @Test
    public void test5() {
        //查询用户名为jerry的用户,或者Id等于5或者50的数据
        //ID=50是不存在的,所以只会查询出2条数据
        DynamicSql<UserEntity> dynamicSql = DynamicSql.createDynamicSql();
        dynamicSql.andEqualTo(UserEntity::getUsername, "jerry");
        //使用此方法创建组查询,此方法可以根据业务无线嵌套
        dynamicSql.orComplex(sql -> sql.andEqualTo(UserEntity::getId, 5).orEqualTo(UserEntity::getId, 50)//.orComplex():这里扔可以选择嵌套括号组
        );
        List<UserEntity> entities = BraveSql.build(dynamicSql, UserEntity.class).select();
        System.out.println(entities);
        System.out.println(entities.size());
    }

5. 更新

5.1 根据条件更新

    @Test
    public void test1() {
        //将用户名 list_0 改为 tom
        UserEntity userEntity = new UserEntity();
        userEntity.setUsername("tom");
        DynamicSql<UserEntity> dynamicSql = DynamicSql.createDynamicSql();
        dynamicSql.andEqualTo(UserEntity::getUsername, "list_0");
        //这里会生成语句: update `t_user` set `username` = ? where `username` = ?
        int updated = BraveSql.build(dynamicSql, UserEntity.class).updateActive(userEntity);
        Assert.assertEquals(updated, 1);
    }

5.2 根据主键更新

    @Test
    public void test2() {
		//将ID=3的用户名改为duck
		UserEntity userEntity = new UserEntity();
		userEntity.setUsername("duck");
		//设置主键值
		userEntity.setId(3L);
		userEntity.setCreateDate(LocalDateTime.now());
		userEntity.setUpdateDate(LocalDateTime.now());
		int updated = BraveSql.build(UserEntity.class).updateByPrimaryKey(userEntity);
		Assert.assertEquals(updated, 1);
    }  

5.3 批量更新

    @Test
    public void test3() {
		//将所有用户的密码改为 123@abc
		UserEntity userEntity = new UserEntity();
		userEntity.setPassword("123@abc");
		BraveSql.build(UserEntity.class).updateActive(userEntity);
	}

6. 删除

6.1 根据条件删除

    @Test
    public void test1() {
        //删除ID等于7、8、9的用户
        DynamicSql<UserEntity> dynamicSql = DynamicSql.createDynamicSql();
        dynamicSql.andIn(UserEntity::getId, Arrays.asList(7, 8, 9));
        BraveSql.build(dynamicSql, UserEntity.class).delete();
    }

6.2 根据主键删除

    @Test
    public void test2() {
        //删除ID等于 10的用户
        Integer deleted = BraveSql.build(UserEntity.class).deleteByPrimaryKey(10);
        Assert.assertTrue(deleted == 1);
    }