PetaPojo 使用指南
A tiny ORM-ish thing for your POJOs
背景
由于工作的一些原因焰檩,需要從C#轉(zhuǎn)成JAVA。之前PetaPoco用得真是非常舒服,在學(xué)習(xí)JAVA的過(guò)程中熟悉了一下JAVA的數(shù)據(jù)組件:
-
MyBatis
非常流行,代碼生成也很成熟,性能也很好苇侵。但是DEBUG的時(shí)候不方便,且XML寫(xiě)SQL也不是很適應(yīng)企锌,尤其是團(tuán)隊(duì)比較小沒(méi)有專(zhuān)職DBA的情況下。 -
Hibernate
使用過(guò)NHibernate于未,做企業(yè)應(yīng)用倒是挺適合的撕攒。掌握并用好它不是一件很容易的事情陡鹃,尤其是團(tuán)隊(duì)水平不夠,目標(biāo)項(xiàng)目為互聯(lián)網(wǎng)游戲平臺(tái)的時(shí)候抖坪。 -
sql2o
開(kāi)源項(xiàng)目萍鲸,輕量級(jí)的ORM,與Dapper擦俐,PetaPoco非常類(lèi)似脊阴,感覺(jué)上還是沒(méi)有PetaPoco好用。
基于以上的理解蚯瞧,便打算造一個(gè)JAVA版的PetaPoco —— PataPojo:
- 處于學(xué)習(xí)JAVA階段嘿期,能夠?qū)W習(xí)并使用一些高級(jí)語(yǔ)法
- 理解JAVA的基礎(chǔ)數(shù)據(jù)庫(kù)組件
- 若開(kāi)發(fā)成功,則原有的團(tuán)隊(duì)與項(xiàng)目基本上能夠很好地遷移至JAVA開(kāi)發(fā)環(huán)境埋合,提升開(kāi)發(fā)效率
功能概述
- 輕量級(jí)
- 用于簡(jiǎn)單的POJO
- 泛型的增刪改查的幫助方法提供
- 自動(dòng)分頁(yè)备徐,自動(dòng)計(jì)算出總記錄數(shù)及頁(yè)數(shù)據(jù)
- 簡(jiǎn)單的事務(wù)支持
- 暫時(shí)僅支持MYSQL
- 更好的參數(shù)支持
- 仍然使用SQL語(yǔ)法,并提供強(qiáng)大的Sql生成器類(lèi)
- 開(kāi)源
下載
- GitHub:https://www.github.com/
怎么使用
引用jar包
下載petapojo.jar
包后甚颂,在項(xiàng)目中進(jìn)行引用
執(zhí)行查詢(xún)
實(shí)體Mapping
// 對(duì)應(yīng)表 user_info
@TableName("user_info")
// 主鍵映射蜜猾,字段名,是否自增
@PrimaryKey(value = "id",autoIncrement = true)
public class UserInfo {
private int id;
private String userName;
// 列名與字段名的映射 (若無(wú)注解振诬,則按字段名映射為列名)
@Column("password")
private String password;
// 該字段不進(jìn)行映射
@Ignore
private String none;
//... getter and setter
}
下一步蹭睡,使用database.java進(jìn)行查詢(xún):
DruidDataSource dataSource = new DruidDataSource();
// 配置dataSource
// ...
Database database = new Database(dataSource);
List<UserInfo> userList = database.query(UserInfo.class, "SELECT * FROM user_info");
for (UserInfo userInfo : userList) {
System.out.println(userInfo.getUserName());
}
查詢(xún)第一行第一列:
Integer number = database.executeScalar(Integer.class,
"SELECT COUNT(1) FROM user_info");
或者,查找第一條記錄:
UserInfo userInfo = database.firstOrDefault(UserInfo.class,
"SELECT * FROM user_info WHERE id = ?",123);
分頁(yè)
// 參數(shù)說(shuō)明:
// 1 - 頁(yè)索引
// 10 - 頁(yè)大小
// 20 - 是指 age > ? 的參數(shù)值
PageInfo<UserInfo> pagedList = database.pagedList(UserInfo.class, 1,10,
"SELECT * FROM user_info WHERE age > ? ORDER BY createDate DESC",20);
返回的PageInfo
描述:
/**
* 分頁(yè)泛型
*/
public class PageInfo<T> {
// 當(dāng)前頁(yè)索引
private int currentPage;
// 總頁(yè)數(shù)
private int totalPages;
// 總記錄數(shù)
private int totalItems;
// 每頁(yè)記錄數(shù)
private int itemsPrePage;
// 當(dāng)前頁(yè)列表
private List<T> items;
public PageInfo() {
items = new ArrayList<>();
}
// ...
// getter and setter
}
執(zhí)行沒(méi)有返回的SQL:
// 返回影響行數(shù)
int row = database.executeUpdate("DELETE FROM user_info WHERE id = ?",123);
實(shí)體的增刪改
新增:
UserInfo userInfo = new UserInfo();
userInfo.setUserName("PetaPojo");
userInfo.setPassword("123123");
database.insert(userInfo);
修改:
UserInfo userInfo = database.firstOrDefault(UserInfo.class,
"SELECT * FROM user_info WHERE id = ?", 1);
userInfo.setPassword("123456");
database.update(userInfo);
刪除:
UserInfo userInfo = database.firstOrDefault(UserInfo.class,
"SELECT * FROM user_info WHERE id = ?", 1);
database.delete(userInfo);
// 或者 根據(jù)主鍵刪除
database.delete(UserInfo.class,1);
// 或者 根據(jù)條件進(jìn)行刪除
database.delete(UserInfo.class,"WHERE id = ?",1);
自動(dòng)添加查詢(xún)列
當(dāng)我們?cè)谑褂肙RM時(shí)赶么,我們常常需要先編寫(xiě)查詢(xún)列名及表名的SQL語(yǔ)句SELECT * FROM user_info
肩豁,其實(shí)是非常影響開(kāi)發(fā)效率的。
因此禽绪,PetaPojo
增加了自動(dòng)添加查詢(xún)列與表名
的自動(dòng)匹配功能蓖救。
例如:
UserInfo userInfo = database.firstOrDefault(UserInfo.class,
"SELECT * FROM user_info WHERE id = ?", 1);
PetaPojo
可以允許簡(jiǎn)化為:
UserInfo userInfo = database.firstOrDefault(UserInfo.class,"WHERE id = ?",1);
查詢(xún)組裝器 Sql Builder
在我們查詢(xún)數(shù)據(jù)庫(kù)時(shí),經(jīng)常需要添加一些條件或排序之類(lèi)的印屁⊙啵總之,盡可能地讓SQL語(yǔ)句動(dòng)態(tài)化或更靈活雄人,以應(yīng)對(duì)復(fù)雜的業(yè)務(wù)需要从橘。
與此同時(shí),如果我們只是進(jìn)行單純的SQL硬編寫(xiě)础钠,開(kāi)發(fā)效率將會(huì)是一個(gè)很大的問(wèn)題恰力,維護(hù)亦比較復(fù)雜費(fèi)時(shí)。
在此基礎(chǔ)上旗吁,PetaPojo
提供了一個(gè)非常便捷的SQL查詢(xún)組裝器踩萎。
基礎(chǔ)模式:
int id = 1;
Sql sql = Sql.create()
.append("SELECT * FROM user_info")
.append("WHERE id = ?",id);
UserInfo userInfo = database.firstOrDefault(UserInfo.class,sql);
或者:
int id = 1;
Sql sql = Sql.create()
.append("SELECT * FROM user_info")
.append("WHERE id = ?", id)
.append("AND createDate >= ?", DateTime.now());
UserInfo userInfo = database.firstOrDefault(UserInfo.class, sql);
同樣,可以根據(jù)不同的條件來(lái)進(jìn)行組裝:
int id = 1;
Sql sql = Sql.create()
.append("SELECT * FROM user_info")
.append("WHERE id <> ?", id)
if(age != null)
sql.append("AND age > ?", age);
if(startDate != null)
sql.append("AND createDate >= ?", startDate);
List<UserInfo> userList = database.query(UserInfo.class,sql);
在SQL組裝時(shí)很钓,參數(shù)列表是無(wú)限的香府,只需要與SQL語(yǔ)句中的參數(shù)替代符?
相匹配即可董栽,如:
int id = 1;
DateTime now = DateTime.now();
Sql sql = Sql.create()
.append("SELECT * FROM user_info")
.append("WHERE id <> ? AND createDate >= ?", id, now);
List<UserInfo> userList = database.query(UserInfo.class,sql);
在Sql.append
的基礎(chǔ)上,PetaPojo
提供了更為便捷的鏈?zhǔn)胶瘮?shù):
Sql sql = Sql.create()
.select("*")
.from("user_info")
.where("id = ?", 1)
.where("createDate >= ?", DateTime.now().toString())
.where("age >= ? AND age <= ?", 10, 20)
.orderBy("createDate DESC");
PageInfo<UserInfo> pagedList = database.pagedList(UserInfo.class,1,10,sql);
基于自動(dòng)添加查詢(xún)列
的功能企孩,上述語(yǔ)句可變改為:
Sql sql = Sql.create()
.where("id = ?", 1)
.where("createDate >= ?", DateTime.now())
.where("age >= ? AND age <= ?", 10, 20)
.orderBy("createDate DESC");
PageInfo<UserInfo> pagedList = database.pagedList(UserInfo.class,1,10,sql);
因此锭碳,在一些復(fù)雜的查詢(xún)中,我們可這樣用:
int id = 1;
Sql sql = Sql.create()
.where("id <> ?", id)
if(age != null)
sql.where("age > ?", age);
if(startDate != null)
sql.where("createDate >= ?", startDate);
sql.orderBy("createDate DESC");
List<UserInfo> userList = database.query(UserInfo.class,sql);
枚舉支持
在開(kāi)發(fā)中勿璃,經(jīng)常會(huì)碰到一些需要使用枚舉的地方擒抛,如訂單狀態(tài),用戶(hù)類(lèi)型等补疑。
JAVA默認(rèn)枚舉類(lèi)型不是很好用歧沪,最主要的問(wèn)題在于:用戶(hù)類(lèi)型,訂單狀態(tài)這些枚舉在系統(tǒng)中我們可以理解為一個(gè)鍵值對(duì)的列表癣丧,而JAVA默認(rèn)枚舉的index是不可自定義的槽畔。
因此,PetaPojo
在枚舉的支持上做了一些擴(kuò)展胁编。
首先厢钧,PetaPojo
定義了一個(gè)枚舉接口:
/**
* 枚舉必須要實(shí)現(xiàn)的接口
*/
public interface IEnumMessage {
int getValue();
String getName();
}
并提供了一個(gè)枚舉幫助類(lèi):
public class EnumUtils {
private static final ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
private static final ReentrantReadWriteLock.ReadLock rl = rrwl.readLock();
private static final ReentrantReadWriteLock.WriteLock wl = rrwl.writeLock();
private static Map<Class<? extends IEnumMessage>, Map<Integer, IEnumMessage>> ENUM_MAPS = new HashMap<>();
/**
* 根據(jù)key以及枚舉類(lèi)型,得到具體的枚舉對(duì)象
*/
public static <T extends IEnumMessage> T getEnum(Class<T> type, int value) {
return (T) getEnumValues(type).get(value);
}
/**
* 將枚舉類(lèi)型轉(zhuǎn)化為一個(gè)鍵值對(duì)的Map
*/
public static <T extends IEnumMessage> Map<Integer, String> getEnumItems(Class<T> type) {
Map<Integer, IEnumMessage> map = getEnumValues(type);
Map<Integer, String> resultMap = new HashMap<>();
map.forEach((k, v) -> {
resultMap.put(k, v.getName());
});
return resultMap;
}
private static <T extends IEnumMessage> Map<Integer, IEnumMessage> getEnumValues(Class<T> clazz) {
rl.lock();
try {
if (ENUM_MAPS.containsKey(clazz))
return ENUM_MAPS.get(clazz);
} finally {
rl.unlock();
}
wl.lock();
try {
if (ENUM_MAPS.containsKey(clazz))
return ENUM_MAPS.get(clazz);
Map<Integer, IEnumMessage> map = new HashMap<>();
try {
for (IEnumMessage enumMessage : clazz.getEnumConstants()) {
map.put(enumMessage.getValue(), enumMessage);
}
} catch (Exception e) {
throw new RuntimeException("getEnumValues error", e);
}
ENUM_MAPS.put(clazz, map);
return map;
} finally {
wl.unlock();
}
}
}
進(jìn)而嬉橙,在項(xiàng)目使用枚舉(如UserType
)時(shí)早直,實(shí)現(xiàn)IEnumMessage接口即可:
/**
* 用戶(hù)類(lèi)型
*/
public enum UserType implements IEnumMessage {
Student(1,"學(xué)生"),
Teacher(2,"老師"),
Coder(4,"碼農(nóng)");
private int value;
private String name;
UserType(int value,String name){
this.value = value;
this.name = name;
}
@Override
public int getValue() {
return this.value;
}
@Override
public String getName() {
return this.name;
}
}
對(duì)應(yīng)的實(shí)體:
@TableName("user_info")
@PrimaryKey(value = "id",autoIncrement = true)
public class UserInfo {
private int id;
private String userName;
private String password;
// 直接使用枚舉,數(shù)據(jù)庫(kù)中作為int型進(jìn)行存儲(chǔ)
private UserType userType;
// ... getter setter
}
UserInfo userInfo = database.firstOrDefault(UserInfo.class,
"SELECT * FROM user_info WHERE id = ?",1);
// 設(shè)置用戶(hù)類(lèi)型為碼農(nóng)
userInfo.setUserType(UserType.Coder);
database.update(userInfo);
時(shí)間類(lèi)型支持 org.joda.time.DateTime
PetaPojo
支持org.joda.time.DateTime
類(lèi)型市框,映射的數(shù)據(jù)庫(kù)表字段類(lèi)型為datetime
:
@TableName("user_info")
@PrimaryKey(value = "id",autoIncrement = true)
public class UserInfo {
private int id;
private String userName;
private String password;
// 直接使用枚舉霞扬,數(shù)據(jù)庫(kù)中作為int型進(jìn)行存儲(chǔ)
private UserType userType;
private DateTime createDate;
// ... getter setter
}
當(dāng)SQL查詢(xún)中的參數(shù)值為org.joda.time.DateTime
時(shí),這樣使用:
Sql sql = Sql.create()
.where("createDate >= ?",DateTime.now().toString());
// 將DateTime類(lèi)型toString即可
以上便是整體PetaPojo
的主要功能枫振。