轻量级数据库访问框架FastSQL

目录

  • 1 简介
  • 2 入门
  • 3 SQLFactory的配置
  • 4 SQL类作为sql语句构建器
  • 5 SQL类的执行sql功能
  • 6 BaseDAO
  • 7 通用工具
  • 8 配置项
  • 9 其他
  • 10 更新日志

1 简介

FastSQL一个基于spring-jdbc的简单ORM框架,它支持sql构建、sql执行、命名参数绑定、查询结果自动映射和通用DAO。结合了Hibernate/JPA快速开发和Mybatis高效执行的优点。

FastSQL可以完全满足你控制欲,可以用Java代码清晰又方便地写出sql语句并执行。

FastSQL完全可用于生产环境,相比Mybatis,使用简单,开发效率高。

2 入门

2.1 安装

如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:

<dependency>
    <groupId>top.fastsqlgroupId>
    <artifactId>fastsqlartifactId>
    <version>1.3.0version>
dependency>

如果使用 Gradle 来构建项目,则需将下面的代码置于 build.gradle 文件的 dependencies 代码块中:

compile 'top.fastsql:fastsql:1.3.0'

2.2 构建 SQLFactory

你可以直接从 Java 程序构建一个 SQLFactory ,如果使用SQL的执行功能,至少需要设置 DataSource 。

//新建一个DataSource(这里使用了spring-jdbc的SimpleDriverDataSource)
//也可以使用支持连接池的各种DataSource,如DruidDataSource等
DataSource dataSource = new SimpleDriverDataSource(<...省略参数...>);

SQLFactory sqlFactory = new SQLFactory();
sqlFactory.setDataSource(dataSource);

2.3 从 SQLFactory 中获取 SQL

既然有了 SQLFactory ,我们就可以从中获得 SQL 的实例了。SQL类完全包含了面向数据库执行 sql 命令所需的所有方法。
你可以通过 SQL 实例来构建并直接执行 SQL 语句。例如:

SQL sql = sqlFactory.sql();
Student student = sql.SELECT("*").FROM("student").WHERE("id=101").queryOne(Student.class);

2.4 作用域(Scope)和生命周期

SQLFactory

SQLFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。
使用 SQLFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SQLFactory 被视为一种代码“坏味道(bad smell)”。
因此 SQLFactory 的最佳作用域是应用的作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式
(如果在Spring环境中,利用Spring容器的功能,你完全可以把它设置为一个单例bean)。

SQL

SQL 实例是有状态的 ,不是线程安全的,是不能被共享的。即使在同一个线程中每执行sql语句一次,都需要重新构建一个 SQL 实例。
绝对不能将 SQL 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

3 SQLFactory 配置

新建SQLFactory

SQLFactory sqlFactory = new SQLFactory();

指定DataSource

//新建任意类型一个DataSource,如SimpleDriverDataSource(Spring内部提供的)
//  或者其他支持连接池的DataSource
DataSource dataSource =  ... ;

//设置数据源
sqlFactory.setDataSource(dataSource);

设置数据源类型

sqlFactory.setDataSourceType(DataSourceType.POSTGRESQL);//默认
//支持 DataSourceType.POSTGRESQL、 DataSourceType.MY_SQL、DataSourceType.ORACLE

其他设置

sqlFactory.setQueryTimeout(5000);//设置最大超时时间
sqlFactory.setMaxRows(100);//查询最大行数

4 使用SQL类构建sql语句

Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL语句。SQL类可以简化你构建sql语句的过程。

4.1 基本查询

SELECT方法可以传入一个可变参数,以便选择多列。(FastSQL中建议SQL关键字全部采用大写)

sqlFactory.sql().SELECT("name", "age").FROM("student").WHERE("age>10").build();
//==> SELECT name,age FROM student WHERE age>10
sqlFactory.sql().SELECT("name", "age").FROM("student").WHERE("name='小红'").build();
//==> SELECT name,age FROM student WHERE name='小红'

WHERE()关键字生成WHERE 1=1语句,动态sql构建如下

SQL sql = sqlFactory.sql().SELECT("name", "age").FROM("student").WHERE();
if (true){
  sql.AND("age > 10");
}
if (false){
  sql.AND("age < 8");
}

//生成sql=>SELECT name,age  FROM student  WHERE 1 = 1  AND age > 10

4.2 使用操作符方法

FastSQL提供了一些操作符方便SQL的构建,比如:

sqlFactory.sql()
    .SELECT("name", "age")
    .FROM("student")
    .WHERE("age").lt("10")
    .AND("name").eq("'小明'")
    .build();
//生成sql=> SELECT name,age FROM student WHERE age < 10 AND name = '小明'

如下:

方法 说明
eq(String) 生成 = ,并追加参数(equal的缩写)
gt(String) 生成 > ,并追加参数(是greater than的缩写)
gtEq(String) 生成 >= ,并追加参数(是greater than or equal的缩写)
lt(String) 生成 < ,并追加参数(是less than的缩写 )
ltEq(String) 生成 <= ,并追加参数(是less than or equal的缩写)
nEq(String) 生成 != ,并追加参数(是not equal的缩写 )
LIKE(String) 生成 LIKE ,并追加参数,
NOT_LIKE(String) 生成 NOT LIKE ,并追加参数
IS_NULL() 生成 IS NULL
IS_NOT_NULL() 生成 IS NOT NULL
eq() 生成 =
gt() 生成 >
gtEq() 生成 >=
lt() 生成 <
ltEq() 生成 <=
nEq() 生成 !=
LIKE() 生成 LIKE
NOT_LIKE() 生成 NOT LIKE

byType(Object)

这些方法仅仅是字符串连接:eq("1")生成= 1eq("'1'")会生成= '1'。byType(Object)方法可以根据类型生成你想要的sql字符串

sqlFactory.sql()
        .SELECT("name", "age")
        .FROM("student")
        .WHERE("age").lt().byType(10)
        .AND("name").eq().byType("小明")
        .build();
//==>SELECT name,age FROM student WHERE age < 10 AND name = '小明'
方法 说明
byType(Object) 根据类型生成相应字符串 ,如 byType(1)生成1 ,byType(“1”)生成’1’
eqByType(Object) 使用 = 连接根据类型生成相应的字符串

4.3 使用连接查询/排序

查询不及格的成绩

sqlFactory.sql().SELECT("s.name","c.subject_name","c.score_value")
        .FROM("score c")
        .LEFT_JOIN_ON("student s", "s.id=c.student_id")
        .WHERE("c.score_value<60")
        .ORDER_BY("c.score_value")
        .build();
/*
生成sql =>
SELECT s.name, c.subject,c.score_value
FROM score c
LEFT OUTER JOIN student s ON (s.id = c.student_id)
WHERE c.score_value < 60
ORDER BY c.score_value
*/

4.4 分组查询

查询每个学生总分数

sqlFactory.sql().SELECT("s.name", "sum(c.score_value) total_score")
        .FROM("score c")
        .LEFT_JOIN_ON("student s", "s.id=c.student_id")
        .GROUP_BY("s.name")
        .build()
/*
生成sql==>

SELECT s.name, sum(c.score_value) total_score
FROM score c
LEFT OUTER JOIN student s ON (s.id = c.student_id)
GROUP BY s.name
*/

4.5 IN语句

由于Jdbc规范不支持IN参数绑定,FastSQL提供了几种IN语句直接拼接的方式:

//1.使用字符串
sqlFactory.sql().SELECT("*")
   .FROM("student")
   .WHERE("name").IN("('小明','小红')")
   .build();

//2.使用集合(List,Set等)
sqlFactory.sql().SELECT("*")
   .FROM("student")
   .WHERE("name").IN(Lists.newArrayList("小明","小红"))
   .build();

//3.使用数组
sqlFactory.sql().SELECT("*")
   .FROM("student")
   .WHERE("name").IN(new Object[]{"小明","小红"})
   .build();

//4.使用可变参数(最简洁)
sqlFactory.sql().SELECT("*")
   .FROM("student")
   .WHERE("name").IN_var("小明","小红")
   .build();

//生成sql==> SELECT *  FROM student  WHERE name  IN ('小明','小红')

4.6 使用KaTeX parse error: Expected group after '_' at position 1: _̲()方法进行子查询

查询大于平均分的成绩(可以使用 KaTeX parse error: Expected group after '_' at position 1: _̲()方法)

sqlFactory.sql().SELECT("*")
   .FROM("score")
   .WHERE("score_value >")
   .$_$(
         sqlFactory.sql().SELECT("avg(score_value)").FROM("score")
    )
   .build();
//生成sql==>
//SELECT *  FROM score
//WHERE score_value >  ( SELECT avg(score_value)  FROM score  )

带有IN的子查询

sqlFactory.sql().SELECT("*")
    .FROM("score")
    .WHERE()
    .AND("score")
    .IN().$_$(
         sqlFactory.sql().SELECT("DISTINCT score_value").FROM("score")
    )
    .build();
//生成sql==> SELECT * FROM score WHERE 1 = 1 AND score IN (SELECT DISTINCT score_value FROM score)

注:复杂sql推荐使用字符串直接构建:

String sql="SELECT * FROM score WHERE 1 = 1 AND score IN (SELECT DISTINCT score_value FROM score)";
sqlFactory.sql().str(sql);

4.7 AND和OR结合使用

如果查询年龄大于10岁,并且名字是小明或小红

sqlFactory.sql().SELECT("*")
   .FROM("student")
   .WHERE("age>10")
   .AND("(name='小明' OR name='小红')")//手动添加括号
   .build();
//或者
sqlFactory.sql().SELECT("*")
   .FROM("student")
   .WHERE("age>10")
   .AND().$_$("name='小明' OR name='小红'")//$_$ 生成左右括号
   .build();

4.8 使用Lambda表达式简化构建动态sql

  • ifTrue(boolean bool, Consumer sqlConsumer):如果第1个参数为true,则执行第二个参数(Lambda表达式)
  • ifNotEmpty(Collection collection, Consumer sqlConsumer):如果第1个参数长度大于0,则执行第二个参数(Lambda表达式)
  • ifPresent(Object object, Consumer sqlConsumer):如果第1个参数存在(不等于null且不为""),则执行第二个参数(Lambda表达式)
  • ifEquals(Object object1, Object object2, Consumer sQLConsumer):如果前两个参数相等,则执行lambda表达式
sqlFactory.sql()
    .SELECT("student")
    .WHERE("id=:id")
    .ifTrue(true, sql -> thisBuilder.AND("name=:name"))
    .ifNotEmpty(names, sql -> {
        System.out.println("ifNotEmpty?");
        thisBuilder.AND("name").IN(Lists.newArrayList("小明", "小红"));
    })
    .ifPresent("",sql -> {
        System.out.println("ifPresent?");
        //...处理其他流程语句...
    })
    .build();

输出:

ifNotEmpty?
SELECT student WHERE id=:id AND name=:name AND name  IN ('小明','小红')

4.9 分页功能

使用原生关键字进行分页

sqlFactory.sql().SELECT("*").FROM("student").LIMIT(10).build();
sqlFactory.sql().SELECT("*").FROM("student").LIMIT(5, 10).build(); //mysql中的写法
sqlFactory.sql().SELECT("*").FROM("student").LIMIT(10).OFFSET(5).build(); //postgresql中的写法

生成如下SQL

SELECT * FROM student LIMIT 10;
SELECT * FROM student LIMIT 5,10;
SELECT * FROM student LIMIT 10 OFFSET 5;

使用 pageThis(int,int) 分页方法进行分页

//
sqlFactory.setDataSourceType(DataSourceType.POSTGRESQL); //使用枚举指定数据源类型
sqlFactory.sql().SELECT("*").FROM("student").pageThis(1,10).build();

注意:如果不指定 dataSourceType,将会默认使用 postgresql 数据库类型进行分页;

使用 countThis() 生成获取数量语句

//countThis
sqlFactory.sql().SELECT("*").FROM("student").countThis().buildAndPrintSQL();

4.10 构建插入insert/修改update/删除delete语句

插入

//使用列
sqlFactory.sql().INSERT_INTO("student", "id", "name", "age")
                .VALUES("21", "'Lily'", "12").build();
//=>INSERT INTO student (id,name,age)  VALUES (21,'Lily',12)

//不使用列
sqlFactory.sql().INSERT_INTO("student").VALUES("21", "'Lily'", "12").build();
//=>INSERT INTO student VALUES (21,'Lily',12)

修改

SET(String…items) :SET关键字

sqlFactory.sql().UPDATE("student").SET("name = 'Jack'","age = 9").WHERE("name = 'Mike'").build();
//=>  UPDATE student SET name = 'Jack',age = 9 WHERE name = 'Mike'

构建删除语句

sqlFactory.sql().DELETE_FROM("student").WHERE("id=12").build();
//=>DELETE FROM student WHERE id=12

5 使用SQL类执行sql语句

5.1 创建SqlFactory

//创建任意DataSource对象(这里使用了spring自带的数据源SimpleDriverDataSource)
DataSource dataSource = new SimpleDriverDataSource(
                new Driver(), "jdbc:postgresql://192.168.0.226:5432/picasso_dev2?stringtype=unspecified",
                "developer", "password");

//创建SqlFactory
SqlFactory sqlFactory = new SqlFactory();
sqlFactory.setDataSource(dataSource);
sqlFactory.setDataSourceType(DataSourceType.MY_SQL);

5.2 设置参数的方法

FastSQL支持多种传入命名参数的方法:

  • parameter(SqlParameterSource) 支持传入SqlParameterSource类型的参数(为了兼容spring-jdbc)。
  • beanParameter(Object)方法可以传入对象参数。
  • mapParameter(Map)支持传入Map类型参数。
  • mapItemsParameter(Object...)支持多个key-value形式的参数,比如mapItemsParameter("id", 12345,"name","小明")
  • beanAndMapParameter(Object, Map) 支持两种不同的参数组合,后一个map会覆盖前面bean中的相同名字的参数。
  • addMapParameterItem(String, Object)可以为以上几种传参方法追加参数。

注意:以上方法除了addMapParameterItem只能调用一次,调用多次会导致前面的传参被覆盖。如需追加参数,请使用addMapParameterItem

FastSQL也支持?占位符和可变参数:

  • varParameter(Object... vars) 传入可变参数,可以调用多次,用来追加参数。

示例

使用beanParameter方法支持传入一个参数bean

public class StudentDTO{
    private String name;
    private int age;
    //省略set和get方法
}
StudentDTO dto =new StudentDTO();
dto.setName="小明";
dto.setAge=10;

sqlFactory.sql().SELECT("*")
    .FROM("student")
    .WHERE("name=:name")
    .AND("age>:age")
    .beanParameter(dto)  //设置一个DTO查询参数
    .queryList(StudVO.class);

使用beanParameter方法并追加参数

Map<String,Object> param = new HashMap<>();
map.put("name","李%");

sqlFactory.sql()
    .SELECT("*")
    .FROM("student")
    .WHERE("name").LIKE(":name")
    .AND("age > :age")
    .beanParameter(param)  //设置一个map参数
    .addParameterMapItem("age",12) //追加
    .queryList(Student.class);

使用varParameter方法–支持?占位符和可变参数

SQL sql = sqlFactory.sql();
sql.INSERT_INTO("student", "id", "name", "age")
    .VALUES("?", "?", "?")
    .varParameter("123", "小明")
    .varParameter(12)
    .update();

5.3 查询方法

5.3.1 查询方法解析

  • T queryOne(Class returnClassType)查询单行结果封装为一个对象,参数可以是可以为String/Integer/Long/Short/BigDecimal/BigInteger/Float/Double/Boolean或者任意POJO的class。
  • Map queryMap()查询单行结果封装为Map
  • List queryList(Class returnClassType)查询多行结果封装为一个对象列表
  • List> queryMapList()查询多行结果封装为Map数组
  • List queryArrayList() 查询结果封装为泛型为Object数组的列表
  • ResultPage queryPage(int page, int perPage, Class returnClassType) 查询结果页

5.3.2 示例

StudentVO是查询视图类,包含name和age字段;StudentDTO是查询参数类,包含name字段。

//queryList可以查询列表,可以是基本类型列表或对象列表
List<String> strings = sqlFactory.sql().SELECT("name")
                .FROM("student")
                .queryList(String.class); //这里执行查询列表并指定返回类型

List<StudVO> studVOList = sqlFactory.sql().SELECT("name", "age")
                            .FROM("student")
                            .WHERE("name=:name")
                            .beanParameter(new StudentDTO())  //设置一个DTO查询参数
                            .queryList(StudVO.class);     //查询一个对象列表

//queryOne可以查询一个值,可以是基本类型  或 对象
String name = sqlFactory.sql().SELECT("name")
                 .FROM("student")
                 .WHERE("id=:id")
                 .AND("name=:name")
                 .mapItemsParameter("id", 12345) //可以传入多个k-v值,,还可以调用parameterMap传入Map参数,
                 .addParameterMapItem("name", "Jack")// 使用addParameterMapItem追加k-v值
                 .queryOne(String.class);  //这里执行查询一个对象(基本类型)并指定返回类型

StudVO studVO = sqlFactory.sql().SELECT("name", "age")
                   .FROM("student")
                   .WHERE("name=:name")
                   .beanParameter(new StudentDTO())  //设置一个DTO
                   .queryOne(StudVO.class);     //查询一个对象

//queryPage查询分页
ResultPage<StudVO> studVOResultPage =sqlFactory.sql().SELECT("name", "age")
                                        .FROM("student")
                                        .queryPage(1, 10, StudVO.class);  //分页查询(第一页,每页10条记录)
//根据特定数据库进行分页查询
ResultPage<StudVO> studVOResultPage =sqlFactory.sql().SELECT("name", "age")
                                        .FROM("student")
                                        .queryPage(1, 10, StudVO.class, DbType.MY_SQL);

注意1:queryOne调用后,如果查询的值不存在是不会抛出EmptyResultDataAccessException,而是返回null,所以要用包装类型接收他的值而不是基本类型,并判断非空性

注意2:queryPage返回的是ResultPage对象

5.3.3 各种RowMapper

RowMapper接口

RowMapper是Spring-jdbc中的接口,它可以将数据中的每一行数据封装成用户定义的类.

public interface RowMapper<T> {

	/**
	 *  Spring会把从数据库中查到的结果集迭代调用,每次迭代都会调用这个方法进行 结果集-对象 转换。
	 */
	T mapRow(ResultSet rs, int rowNum) throws SQLException;
}

SQL类的queryXXX相关的方法支持这个接口,
用法如下:

List<StudentVO> list 
         = sqlFactory.sql().SELECT("name", "age")
                .FROM("student")
                .queryList(new RowMapper<StudentVO>() {
                    @Override
                    public StudentVO mapRow(ResultSet rs, int rowNum) throws SQLException {
                        StudentVO studentVO = new StudentVO();
                        studentVO.setName(rs.getString("name"));
                        studentVO.setAge(rs.getInt("age"));
                        return studentVO;
                    }
                });

BeanPropertyRowMapper实现类(spring-jdbc实现)

自动绑定,需要列名称和Java实体类名字一致,如:属性名 “userName” 可以匹配数据库中的列字段 “USERNAME” 或 “user_name”。
这样,我们就不需要一个个手动绑定了。SQL类的 queryList/queryOne/queryPage当查询结果是一个复合对象时,使用了这个类,大部分时候不必手动调用。

ColumnMapRowMapper实现类(spring-jdbc实现)

将结果封装为一个Map,SQL类的 queryMap/queryMapList大部份使用了这个类,大部分时候不必手动调用。

SingleColumnRowMapper实现类(spring-jdbc实现)

SQL类的 queryList/queryOne ,当结果为一列时使用了这个类,如返回List,List,Long,Date。大部分时候不必手动调用。

PartialBeanPropertyRowMapper实现类(fastsql实现)

能够匹配的如BeanPropertyRowMapper类,剩下的需要手动指定,子类需要重写remainingMap方法。

CombinedBeanPropertyRowMapper实现类(fastsql实现,*测试中)

匹配复合结果,配合注解InnerBeanMapped使用

5.4 增删改操作

使用update方法

//插入
sqlFactory.sql().INSERT_INTO("student", "id", "name", "age")
        .VALUES(":id", ":name", ":age")
        .mapItemsParameter("id", 678, "name", "Kiven", "age", 123)
        .update();

//修改
sqlFactory.sql().UPDATE("student")
        .SET("name",":name")
        .WHERE("id=678")
        .mapItemsParameter("id", 678, "name", "Rose", "age", 123)
        .update();
//删除
sqlFactory.sql().DELETE_FROM("student")
        .WHERE("id=:id")
        .mapItemsParameter("id", 678)
        .update();

5.5 事务管理

手动事务:FastSQL事务管理使用Spring的工具类org.springframework.jdbc.datasource.DataSourceUtils

Connection connection = DataSourceUtils.getConnection(dataSource);//开启事务
connection.setAutoCommit(false);//关闭自动提交

sqlFactory.sql()
     .INSERT_INTO("sys_users", "id").VALUES(":id")
     .mapItemsParameter("id", 456)
     .update();

sqlFactory.sql()
    .INSERT_INTO("sys_users", "id").VALUES(":id")
    .mapItemsParameter("id", 234)
    .update();

//connection.rollback(); //回滚

connection.commit();//提交事务

5.6 批量操作

6 BaseDAO

6.1 数据准备

Entity实体类

注解如下

  1. @Table 非必需,如果不写表名称将会被解析为student(根据类名的下划线形式)
  2. @Id 必须存在,对应表的主键
  3. @Entity 非必需,标识一个实体,为了兼容JPA标准建议加上
@Entity
@Table(name="student")
@Data  //use lombok
public class Student {
    @Id
    private Integer id;
    private String name;
    private Integer age;
    private LocalDate birthday;
    private String homeAddress;
}

新建DAO层数据访问类, 并继承BaseDAO类,会自动继承BaseDAO中的方法(详见第2部分)

DAO类在Spring环境中的使用

DAO层:

@Repository
public class StudentDAO extends BaseDAO<Student,Integer> {

}

如果设置了SQLFactory作为一个Spring的@Bean ,将会自动注入。在Service中,你就可以使用这个DAO:

@Service
public class StudentService {
    @Autowire
    private StudentDAO studentDAO;

    @Transactional //如果需要事务--org.springframework.transaction.annotation.Transactional
    public void test1(){
        studentDAO.XXX();//调用任意方法
    }

}

DAO类在非Spring环境中

public class StudentDAO extends BaseDAO<Student,String> {

}
public class Test  {
    public static void main(String[] args) {
        SQLFactory sqlFactory = ...

        StudentDAO studentDAO= new StudentDAO();
        studentDAO.setSqlFactory(sqlFactory);//手动注入
        //执行操作
        studentDAO.XXX();
    }
}

6.2 基本使用方法 CRUD

CRUD 是四种数据操作的简称:C 表示创建,R 表示读取,U 表示更新,D 表示删除。BaseDAO 自动创建了处理数据表中数据的方法。

数据插入

方法 int insert(E entity),插入对象中的值到数据库,null值在生成的sql语句中会设置为NULL

Student student = new Student();
student.setId(2);
student.setName("小丽");
student.setBirthday(LocalDate.now());//这里使用jdk8时间类型
student.setHomeAddress("");

studentDao.insert(student);//获取保存成功的id

//等价如下SQL语句(注意:age被设置为null)
//INSERT INTO student(id,name,age,birthday,home_address) VALUES (2,'小丽',NULL,'2017-07-11','')

方法 int insertSelective(E entity),插入对象中非null的值到数据库

Student student = new Student();
student.setId(3);
student.setName("小丽");
student.setBirthday(new Date());
student.setHomeAddress("");
studentDao.insertSelective(student);

//等价如下SQL语句(注意:没有对age进行保存,在数据库层面age将会保存为该表设置的默认值,如果没有设置默认值,将会被保存为null )
//===>INSERT INTO student(id,name,birthday,home_address)  VALUES  (3,'小丽','2017-07-11','')

数据修改

方法 int update(E entity) ,根据对象进行更新(null字段在数据库中将会被设置为null),对象中@id字段不能为空

//待更新

方法 int updateSelective(E entity),根据对象进行更新(只更新实体中非null字段),对象中@id字段不能为空

//待更新

方法 int updateByColumn(E entity, String... columns),根据id更新可变参数columns列,对象中@id字段不能为空

Student student = studentDAO.selectOneById(44);
student.setAge(19);
studentDAO.updateByColumn(student,"age");

//===>UPDATE student SET age=? WHERE id=?

数据删除

方法 int deleteOneById(String id) 根据id删除数据

int num = studentDao.deleteOneById(2);//获取删除的行数量
//===>DELETE FROM student WHERE id=2

方法 int deleteAll(),删除某个表所有行

int number = studentDao.deleteAll();//获取删除的行数量
// ===>DELETE FROM student

方法 int[] deleteInBatch(List ids) ,根据id列表批量删除数据(所有删除语句将会一次性提交到数据库)

List<String> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
studentDao.deleteInBatch(ids);//返回成功删除的数量

方法int deleteWhere(String sqlCondition, Object... values),根据条件删除

单条数据查询

方法 E selectOneById(String id)

通过id查询一个对象

Student student = studentDao.selectOneById(4);//查询id为12345678的数据,并封装到Student类中

方法 E selectOneWhere(String sqlCondition, Object... values),通过语句查询(返回多条数据将会抛出运行时异常,为了防止sql语句在service层滥用,可变参数最多支持三个)

Student student = studentDao.selectOneWhere("name=? AND home_address=?", "小明", "成都");

方法 E selectOneWhere(String sqlCondition, SqlParameterSource parameterSource) 查询一条数据

多条数据查询

方法 List selectWhere(String sqlCondition, Object... values),用法与selectOneWhere()相同,可以返回一条或多条数据,可变参数最多支持三个

List<Student> studentList  =  studentDao.selectWhere("name=?", "小明");
List<Student> studentList  =  studentDao.selectWhere("ORDER BY age");
List<Student> studentList  =  studentDao.selectWhere("home_address IS NULL ORDER BY age DESC");
//...

方法 List selectAll() 查询所有

List<Student> allStudents  =  studentDao.selectAll();

方法 List selectWhere(String sqlCondition, SqlParameterSource parameterSource)可以返回一条或多条数据

分页查询

方法 ResultPage selectPageWhere(String sqlCondition, int pageNumber, int perPage, Object... values)

方法 ResultPage selectPageWhere(String sqlCondition, int pageNumber, int perPage, SqlParameterSource parameterSource)

方法 ResultPage selectPage(int pageNumber, int perPage)

其他查询

方法 int countWhere(String sqlCondition, Object... values),通过条件查询数量

int countWhere = studentDao.countWhere("age >= 20"); //查找年龄大于等于20的学生
int countWhere = studentDao.countWhere("age > ?" , 10); //查找年龄大于10的学生

方法 int countWhere(String sqlCondition, SqlParameterSource parameterSource),通过条件查询数量

@Repository
public class BizPhotoDAO extends ApplicationBaseDAO<BizPhotoPO, String> {
    public int countByName() {
        return countWhere("photo_name=:name", new MapSqlParameterSource().addValue("name", "物品照片"));
    }
}

方法 int count() 查询表总数量

6.3 定制你的ApplicationBaseDAO

建议在你的程序中实现ApplicationBaseDAO,可以

  1. 定制一些通用方法
  2. 设置多数据库支持
  3. 设置BaseDAO中的触发器
public abstract class ApplicationBaseDAO<E, ID> extends BaseDAO<E, ID> {
  //添加方法等
}

////我们的StudentDAO此时应该继承ApplicationBaseDAO
@Repository
public class StudentDAO extends ApplicationBaseDAO<Student,Integer> {

}

定制通用方法

如下,增加了一个名为logicDelete的逻辑删除方法,将会作用于继承于它的每个DAO

public abstract class ApplicationBaseDAO<E, ID> extends BaseDAO<E, ID> {
  //...其他方法

  public void logicDelete(ID id) {
      //每个表都有一个defunct,1表示已(逻辑)删除
      namedParameterJdbcTemplate.getJdbcOperations().update("UPDATE " + this.tableName + " SET defunct = 1");
  }

  //...其他方法
}

上面的logicDelete方法使用了tableName这个变量,BaseDAO中的部分可用变量为

Class entityClass; //DAO对应的实体类
Class idClass;  //标识为@Id的主键类型

Logger log; //日志,可以在实现类中直接使用

String className; //实体类名
String tableName; //表名

Field idField;  //@Id对应的字段引用
String idColumnName; //表主键列名

namedParameterJdbcTemplate //jdbc模板

//...待更新

设置多数据源支持

public abstract class OracleApplicationBaseDAO<E, ID> extends BaseDAO<E, ID> {
      //重写setSqlFactory方法
      @Autowired
      @Qualifier("sqlFactory1")//===>根据名称注入
      @Override
      protected void setSqlFactory(SQLFactory sqlFactory) {
          super.setSqlFactory(sqlFactory);
      }
}
public abstract class MySqlApplicationBaseDAO<E, ID> extends BaseDAO<E, ID> {
      //重写setSqlFactory
      @Autowired
      @Qualifier("sqlFactory2")//===>根据名称注入
      @Override
      protected void setSqlFactory(SQLFactory sqlFactory) {
          super.setSqlFactory(sqlFactory);
       }}
}

设置BaseDAO中的拦截器

public abstract class ApplicationBaseDAO<E, ID> extends BaseDAO<E, ID> {
    public ApplicationBaseDAO() {
         // 1.设置触发器开关
         this.useBeforeInsert = true; //在插入前执行
         this.useBeforeUpdate = true; //在更新前执行
    }

    //2.重写触发器相关方法
    @Override
    protected void beforeInsert(E object) {
        EntityRefelectUtils.setFieldValue(object, "createdAt", LocalDateTime.now());
        EntityRefelectUtils.setFieldValue(object, "updatedAt", LocalDateTime.now());//在插入数据时自动写入createdAt,updatedAt
    }

    @Override
    protected void beforeUpdate(E object) {
        EntityRefelectUtils.setFieldValue(object, "updatedAt", LocalDateTime.now());//在更新数据时自动更新updatedAt
    }
}

对应关系如下:

count 参数表示执行成功的条数

启用 需重写的方法 作用于
useBeforeInsert beforeInsert(E entity) insertSelective(…)/insert(…)执行插入之前
useAfterInsert afterInsert(E entity,int count) insertSelective(…)/insert(…)执行插入之后
useBeforeUpdate beforeUpdate(E entity) updateSelective(…)/update(…)执行更新之前
useAfterUpdate afterUpdate(E entity,int count) updateSelective(…)/update(…)/updateByColumn(…)执行更新之后
useBeforeDelete beforeDelete(ID id) deleteOneById(…)执行删除之前
useAfterDelete void afterDelete(ID id,int count) deleteOneById(…)执行删除之后

6.4 SQL构建器在BaseDAO中的使用

BaseDAO整合了SQL构建器,在继承BaseDAO的类中你可以你可以直接调用 getSQL() 来获取一个SQL的实例:

@Repository
public class StudentDAO extends ApplicationBaseDAO<Student, String> {
    /**
     * 查询姓李的同学列表
     */
    public List<Student> queryListByName() {
        return getSQL().SELECT("*").FROM(this.tableName)
                       .WHERE("name").LIKE("'李%'")
                       .queryList(Student.class);//查询列表
    }
    
    /**
    * 根据旧名字修改学生姓名
    */
    public int updateName(String oldName,String newName) {
        return getSQL().UPDATE(this.tableName).SET("name = '"+newName+"'").WHERE("name").eqByType(oldName).update();
    }
}

7 通用工具

7.1 获取sql的IN列表

FastSQLUtils.getInClause(Collection collection),会根据Collection的类型自动判断使用什么样的分隔符:

FastSQLUtils.getInClause(Lists.newArrayList(1, 23, 4, 15));
//生成=>(1,23,4,15)

FastSQLUtils.getInClause(Lists.newArrayList("dog", "people", "food", "apple")); 
//生成=> ('dog','people','food','apple')

说明:IN功能已经整合到SQL构建器的IN方法

7.2 获取LIKE通配符

FastSqlUtils.bothWildcard("李"); // => %李%
FastSqlUtils.leftWildcard("李");  // => %李
FastSqlUtils.rightWildcard("李"); // => 李%

8 配置项

显示sql日志,需要调节相应的类日志级别:
org.springframework.jdbc.core.JdbcTemplate 日志级别调整为 debug 会显示SQL语句
org.springframework.jdbc.core.StatementCreatorUtils 日志级别调整为 trace 会显示绑定参数过程

下面是Springboot中的配置:

#显示sql
logging.level.org.springframework.jdbc.core.JdbcTemplate=debug
#显示绑定的参数
logging.level.org.springframework.jdbc.core.StatementCreatorUtils=trace

9 其他

  • 项目主页
  • 版本下载

你可能感兴趣的:(【JavaSE】)