時(shí)隔 6 年,曾經(jīng)的祝福區(qū)公眾號(hào)開始想寫代碼了
最近我在思考對(duì)我的一個(gè)項(xiàng)目進(jìn)行更新,這個(gè)項(xiàng)目已經(jīng)有6年的歷史了。我打算采用JSR-269(可插拔注解處理API)重新設(shè)計(jì)一種使用代碼描述SQL的方式。這種方式類似于C#中的Linq,可以實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的操作。
在后續(xù)的更新中,會(huì)嘗試分享一下反射、MyBatis、APT 以及 ASM 的一些小知識(shí)
以下是 6 年前的功能
ad88 88
d8" ,d 88
88 88 88
MM88MMM ,adPPYYba, ,adPPYba, MM88MMM ,adPPYba, ,adPPYb,d8 88
88 "" `Y8 I8[ "" 88 I8[ "" a8" `Y88 88
88 ,adPPPPP88 `"Y8ba, 88 `"Y8ba, 8b 88 88
88 88, ,88 aa ]8I 88, aa ]8I "8a ,d88 88
88 `"8bbdP"Y8 `"YbbdP"' "Y888 `"YbbdP"' `"YbbdP'88 88
88
88
fastsql
一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)工具類,可以簡(jiǎn)化 DB 操作,減少 SQL 語句的書寫,同時(shí)提供將 SQL 轉(zhuǎn)換 Bean 和將 Bean 轉(zhuǎn)換 SQL 的方法,
Apache Maven
<!-- https://mvnrepository.com/artifact/com.github.zyndev/fastsql -->
<dependency>
<groupId>com.github.zyndev</groupId>
<artifactId>fastsql</artifactId>
</dependency>
FastSQL 主要特性如下:
遵循非侵入式原則,設(shè)計(jì)優(yōu)雅或簡(jiǎn)單,極易上手
支持安全查詢,防止SQL注入
支持與主流數(shù)據(jù)庫(kù)連接池框架集成
支持
@Query查詢擁有非常優(yōu)雅的
Page(分頁)設(shè)計(jì)支持單表
ORM查詢支持部分
jpa注解支持動(dòng)態(tài)SQL創(chuàng)建
支持駝峰標(biāo)識(shí)與下劃線標(biāo)識(shí)轉(zhuǎn)換
運(yùn)行環(huán)境要求
jdk1.8+
入門例子
準(zhǔn)備一個(gè)實(shí)體
@Data
@Entity
@Table(name = "tb_user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column
private Integer id;
@Column
private String uid;
@Column(name = "account_name")
private String accountName;
@Column(name = "nick_name")
private String nickName;
@Column
private String password;
@Column
private String phone;
@Column(name = "register_time")
private Date registerTime;
@Column(name = "update_time")
private Date updateTime;
}
DAO接口
/**
* @version 0.0.5
* @author 張瑀楠 [email protected]
*/
@Repository
public interface UserRepository {
@Query("select count(*) from tb_user")
public Integer getCount();
@Query("delete from tb_user where id = ?1")
public Boolean deleteById(int id);
@Query("select count(*) from tb_user where id = ?1")
public Integer getCountByPassword(@Param("password") String password);
@Query("select uid from tb_user where password = ?1 ")
public String getUidByPassword(@Param("password") String password);
@Query("select * from tb_user where id = :id ")
public User getUserById(@Param("id") Integer id);
@Query("select * " +
" from tb_user " +
" where account_name = :accountName ")
public List<User> getUserByAccountName(@Param("accountName") String accountName);
@Query("insert into tb_user(id, account_name, password, uid, nick_name, register_time, update_time) " +
"values(:id, :user.accountName, :user.password, :user.uid, :user.nickName, :user.registerTime, :user.updateTime )")
public int saveUser(@Param("id") Integer id, @Param("user") User user);
@ReturnGeneratedKey
@Query("insert into tb_user(account_name, password, uid, nick_name, register_time, update_time) " +
"values(:user.accountName, :user.password, :user.uid, :user.nickName, :user.registerTime, :user.updateTime )")
public int saveUser(@Param("user") User user);
@Query("select * " +
" from tb_user " +
" where 1=1 " +
" @if(?1 != null ) { and name like concat('%',?1,'%')} ")
public Map<Integer, User> queryUserByName(String name);
}
使用DAO接口.
該項(xiàng)目主要為了和Spring項(xiàng)目整合使用,這里可以直接通過自動(dòng)注入方式使用,后面會(huì)講到如何整合到Spring MVC和Spring Boot項(xiàng)目,暫時(shí)這里使用注入的方式
@Autowired
private UserRepository userRepository;
這里直接調(diào)用指定的方法即可
關(guān)于 @Query 的使用
在上面的示例中,使用了類似JPA的Query方式,這里講解一下
參數(shù)通過兩種方式指定
位置參數(shù)
命名參數(shù)
在上面的Query 語句中,大致可分為以下三類:
不需要參數(shù)
這類查詢不需要參數(shù)
@Query("select count(*) from tb_user")
public Integer getCount();
位置參數(shù)
這類需要初入?yún)?shù),在語句中可以使用 ?1 ?2 等指定
注意:這里從 1 開始計(jì)數(shù),而不是0
@Query("delete from tb_user where id = ?1")
public Boolean deleteById(int id);
命名參數(shù)
使用 @Param 進(jìn)行處理,在語句中可以使用 :xxx :xxxx 等指定
例如:
@Query("select * from tb_user where id = :id ")
public User getUserById(@Param("id") Integer id);
位置參數(shù)和命名參數(shù)可以混用
注意:在沒有查詢到數(shù)據(jù)的情況下,如果返回值是集合類型,返回具體的值不會(huì)是null,而是一個(gè)空集合. 如果是對(duì)象,則返回 null
條件表達(dá)式
根據(jù)業(yè)務(wù)我們經(jīng)常需要?jiǎng)討B(tài)的構(gòu)建sql,例如mybatis的if標(biāo)簽,在這里也提供了一種語法 @if( condition) { statement },其中 condition 為一個(gè)布爾語句,當(dāng)
語句成立時(shí),拼接后面的 statement
例如:
@Query("select * " +
" from tb_user " +
" where 1=1 " +
" @if(?1 != null ) { and name like concat('%',?1,'%')} ")
public List<User> queryUserByName(String name);
當(dāng) name 為null 時(shí),則查詢語句為 select * from tb_user where 1=1,
否則為 select * from tb_user where 1=1 and name like concat('%',?1,'%')
注意
注意: 查詢單個(gè)字段,還支持返回如下類型:
StringByte和byteShort和shortInteger和intLong和longFloat和floatDouble和doubleCharacter和charBoolean和boolean
注解使用
| Annotation | 作用 |
|---|---|
@Query |
標(biāo)識(shí)查詢語句 |
@Param |
標(biāo)識(shí)命名參數(shù) |
@ReturnGeneratedKey |
返回自增主鍵id |
BaseRepository的內(nèi)置方法
這里的entity比如有 @Entity 注解
// 驗(yàn)證id是否存在
<T> boolean existsById(T entity);
// 統(tǒng)計(jì)entity的數(shù)量
<T> long count(T entity);
// 保存一個(gè)entity, null屬性 不插入
<T> int save(T entity);
// 保存多個(gè)entity, null屬性 不插入
<T> int saveAll(Iterable<T> entities);
// 保存一個(gè)entity, 并設(shè)置null屬性 是否插入
<T> int save(T entity, boolean ignoreNull);
// 保存多個(gè)entity, 并設(shè)置null屬性 是否插入
<T> int saveAll(Iterable<T> entities, boolean ignoreNull);
// 刪除一個(gè)對(duì)象
<T> int deleteById(T entity);
// 刪除符合條件的相似對(duì)象
<T> int delete(T entity);
// 刪除多個(gè)對(duì)象,其中每個(gè)對(duì)象必須有id
<T> int deleteAll(Iterable<T> entities);
// 更新一個(gè)對(duì)象,其中對(duì)象必須有id,null 值不更新
<T> int update(T entity);
// 更新一個(gè)對(duì)象,其中對(duì)象必須有id,并設(shè)置null是否更新
<T> int update(T entity, boolean ignoreNull);
// 更新多個(gè)對(duì)象,其中對(duì)象必須有id,null 值不更新
<T> int update(Iterable<T> entities);
// 更新多個(gè)對(duì)象,其中對(duì)象必須有id,并設(shè)置null是否更新
<T> int update(Iterable<T> entities, boolean ignoreNull);
<T> T findById(T entity);
// 只查詢指定的字段
<T> T findById(T entity, String... columns);
<T> List<T> getEntityList(T entity);
<T> List<T> getEntityList(T entity, String... columns);
<T> List<T> getEntityListBySQL(String sql, T entity);
<T> List<T> getEntityListBySQL(String sql, Object[] args, T entity);
<T> PageListContent<T> getEntityPageList(T entity, int pageNum, int pageSize);
<T> PageListContent<T> getEntityPageList(T entity, int pageNum, int pageSize, String orderBy);
<T> PageListContent<T> getEntityPageList(T entity, int pageNum, int pageSize, String orderBy, String... columns);
<T> PageListContent<T> getEntityPageListBySql(String sql, T entity, int pageNum, int pageSize);
<T> PageListContent<T> getEntityPageListBySql(String sql, T entity, int pageNum, int pageSize, String orderBy);
<T> PageListContent<T> getEntityPageListBySql(String sql, Object[] args, T entity, int pageNum, int pageSize, String orderBy);
分頁
BaseRepository內(nèi)置分頁方法
<T> PageListContent<T> getEntityPageList(T entity, int pageNum, int pageSize);
<T> PageListContent<T> getEntityPageList(T entity, int pageNum, int pageSize, String orderBy);
<T> PageListContent<T> getEntityPageList(T entity, int pageNum, int pageSize, String orderBy, String... columns);
<T> PageListContent<T> getEntityPageListBySql(String sql, T entity, int pageNum, int pageSize);
<T> PageListContent<T> getEntityPageListBySql(String sql, T entity, int pageNum, int pageSize, String orderBy);
<T> PageListContent<T> getEntityPageListBySql(String sql, Object[] args, T entity, int pageNum, int pageSize, String orderBy);

