博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
轻量级数据库访问框架FastSQL
阅读量:4291 次
发布时间:2019-05-27

本文共 23712 字,大约阅读时间需要 79 分钟。

目录

1 简介

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

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

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

2 入门

2.1 安装

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

top.fastsql
fastsql
1.3.0

如果使用 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内部提供的)//  或者其他支持连接池的DataSourceDataSource 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>10sqlFactory.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_valueFROM score cLEFT OUTER JOIN student s ON (s.id = c.student_id)WHERE c.score_value < 60ORDER 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_scoreFROM score cLEFT 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<SQL> sqlConsumer):如果第1个参数为true,则执行第二个参数(Lambda表达式)
  • ifNotEmpty(Collection<?> collection, Consumer<SQL> sqlConsumer):如果第1个参数长度大于0,则执行第二个参数(Lambda表达式)
  • ifPresent(Object object, Consumer<SQL> sqlConsumer):如果第1个参数存在(不等于null且不为""),则执行第二个参数(Lambda表达式)
  • ifEquals(Object object1, Object object2, Consumer<SQL> 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() 生成获取数量语句

//countThissqlFactory.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");//创建SqlFactorySqlFactory sqlFactory = new SqlFactory();sqlFactory.setDataSource(dataSource);sqlFactory.setDataSourceType(DataSourceType.MY_SQL);

5.2 设置参数的方法

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

  • parameter(SqlParameterSource) 支持传入SqlParameterSource类型的参数(为了兼容spring-jdbc)。
  • beanParameter(Object)方法可以传入对象参数。
  • mapParameter(Map<String, Object>)支持传入Map类型参数。
  • mapItemsParameter(Object...)支持多个key-value形式的参数,比如mapItemsParameter("id", 12345,"name","小明")
  • beanAndMapParameter(Object, Map<String, Object>) 支持两种不同的参数组合,后一个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
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<T> returnClassType)查询单行结果封装为一个对象,参数可以是可以为String/Integer/Long/Short/BigDecimal/BigInteger/Float/Double/Boolean或者任意POJO的class。
  • Map<String, Object> queryMap()查询单行结果封装为Map
  • List<T> queryList(Class<T> returnClassType)查询多行结果封装为一个对象列表
  • List<Map<String, Object>> queryMapList()查询多行结果封装为Map数组
  • List<Object[]> queryArrayList() 查询结果封装为泛型为Object数组的列表
  • ResultPage<T> queryPage(int page, int perPage, Class<T> returnClassType) 查询结果页

5.3.2 示例

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

//queryList可以查询列表,可以是基本类型列表或对象列表List
strings = sqlFactory.sql().SELECT("name") .FROM("student") .queryList(String.class); //这里执行查询列表并指定返回类型List
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
studVOResultPage =sqlFactory.sql().SELECT("name", "age") .FROM("student") .queryPage(1, 10, StudVO.class); //分页查询(第一页,每页10条记录)//根据特定数据库进行分页查询ResultPage
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
{
/** * Spring会把从数据库中查到的结果集迭代调用,每次迭代都会调用这个方法进行 结果集-对象 转换。 */ T mapRow(ResultSet rs, int rowNum) throws SQLException;}

SQL类的queryXXX相关的方法支持这个接口,

用法如下:

List
list = sqlFactory.sql().SELECT("name", "age") .FROM("student") .queryList(new RowMapper
() {
@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 lombokpublic 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层:

@Repositorypublic class StudentDAO extends BaseDAO
{
}

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

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

DAO类在非Spring环境中

public class StudentDAO extends BaseDAO
{
}
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<String> ids) ,根据id列表批量删除数据(所有删除语句将会一次性提交到数据库)

List
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<E> selectWhere(String sqlCondition, Object... values),用法与selectOneWhere()相同,可以返回一条或多条数据,可变参数最多支持三个

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

方法 List<E> selectAll() 查询所有

List
allStudents = studentDao.selectAll();

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

分页查询

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

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

方法 ResultPage<E> 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),通过条件查询数量

@Repositorypublic class BizPhotoDAO extends ApplicationBaseDAO
{
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
extends BaseDAO
{
//添加方法等}我们的StudentDAO此时应该继承ApplicationBaseDAO@Repositorypublic class StudentDAO extends ApplicationBaseDAO
{
}

定制通用方法

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

public abstract class ApplicationBaseDAO
extends BaseDAO
{
//...其他方法 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
extends BaseDAO
{
//重写setSqlFactory方法 @Autowired @Qualifier("sqlFactory1")//===>根据名称注入 @Override protected void setSqlFactory(SQLFactory sqlFactory) {
super.setSqlFactory(sqlFactory); }}public abstract class MySqlApplicationBaseDAO
extends BaseDAO
{
//重写setSqlFactory @Autowired @Qualifier("sqlFactory2")//===>根据名称注入 @Override protected void setSqlFactory(SQLFactory sqlFactory) {
super.setSqlFactory(sqlFactory); }}}

设置BaseDAO中的拦截器

public abstract class ApplicationBaseDAO
extends BaseDAO
{
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的实例:

@Repositorypublic class StudentDAO extends ApplicationBaseDAO
{
/** * 查询姓李的同学列表 */ public List
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中的配置:

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

9 其他

转载地址:http://sphgi.baihongyu.com/

你可能感兴趣的文章
Java 中 long 和 double 的原子性?
查看>>
Nginx和Apache的对比
查看>>
一文带你看懂Spring事务!
查看>>
Spring Boot实战:拦截器与过滤器
查看>>
夯实基础:关于线程的Callable和Future两个类解析
查看>>
常用的设计模式汇总,超详细!
查看>>
一篇文章了解RPC框架原理
查看>>
技术进阶:ThreadPoolExecutor源码解析之机制原理篇
查看>>
nginx搭建简易负载均衡服务
查看>>
SpringCloud之Feign(Finchley版)
查看>>
应用框架:Spring IOC基于注解启动的分析
查看>>
基于注解@Aspect的AOP实现
查看>>
Redis 高可用集群
查看>>
Redis 布隆过滤器实战「缓存击穿、雪崩效应」
查看>>
Redis持久化存储详解(一)
查看>>
Spring Boot 面试,一个问题就干趴下了!
查看>>
深度解析SpringCloud之服务注册与发现机制(万字深度好文)
查看>>
分布式架构系列: 负载均衡技术详解
查看>>
一道非常棘手的 Java 面试题:i++ 是线程安全的吗
查看>>
MySql索引那些事
查看>>