背景
由于工作的一些原因,需要從C#轉(zhuǎn)成JAVA。之前PetaPoco用得真是非常舒服,在學(xué)習(xí)JAVA的過程中熟悉了一下JAVA的數(shù)據(jù)組件:
MyBatis
非常流行,代碼生成也很成熟,性能也很好。但是DEBUG的時候不方便,且XML寫SQL也不是很適應(yīng),尤其是團(tuán)隊比較小沒有專職DBA的情況下。Hibernate
使用過NHibernate,做企業(yè)應(yīng)用倒是挺適合的。掌握并用好它不是一件很容易的事情,尤其是團(tuán)隊水平不夠,目標(biāo)項目為互聯(lián)網(wǎng)游戲平臺的時候。sql2o
開源項目,輕量級的ORM,與Dapper,PetaPoco非常類似,感覺上還是沒有PetaPoco好用。
基于以上的理解,便打算造一個JAVA版的PetaPoco —— PataPojo:
處于學(xué)習(xí)JAVA階段,能夠?qū)W習(xí)并使用一些高級語法
理解JAVA的基礎(chǔ)數(shù)據(jù)庫組件
若開發(fā)成功,則原有的團(tuán)隊與項目基本上能夠很好地遷移至JAVA開發(fā)環(huán)境,提升開發(fā)效率
功能概述
輕量級
用于簡單的POJO
泛型的增刪改查的幫助方法提供
自動分頁,自動計算出總記錄數(shù)及頁數(shù)據(jù)
簡單的事務(wù)支持
暫時僅支持MYSQL
更好的參數(shù)支持
仍然使用SQL語法,并提供強(qiáng)大的Sql生成器類
開源
下載
GitHub:https://www.github.com/ (額,還在上傳中)
怎么使用
引用jar包
下載petapojo.jar
包后,在項目中進(jìn)行引用
執(zhí)行查詢
實體Mapping
// 對應(yīng)表 user_info@TableName("user_info")// 主鍵映射,字段名,是否自增@PrimaryKey(value = "id",autoIncrement = true)public class UserInfo { private int id; private String userName; // 列名與字段名的映射 (若無注解,則按字段名映射為列名) @Column("password") private String password; // 該字段不進(jìn)行映射 @Ignore private String none; //... getter and setter}
下一步,使用database.java進(jì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()); }
查詢第一行第一列:
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);
分頁
// 參數(shù)說明:// 1 - 頁索引// 10 - 頁大小// 20 - 是指 age > ? 的參數(shù)值PageInfo<UserInfo> pagedList = database.pagedList(UserInfo.class, 1,10, "SELECT * FROM user_info WHERE age > ? ORDER BY createDate DESC",20);
返回的PageInfo
描述:
/** * 分頁泛型 */public class PageInfo<T> { // 當(dāng)前頁索引 private int currentPage; // 總頁數(shù) private int totalPages; // 總記錄數(shù) private int totalItems; // 每頁記錄數(shù) private int itemsPrePage; // 當(dāng)前頁列表 private List<T> items; public PageInfo() { items = new ArrayList<>(); } // ... // getter and setter}
執(zhí)行沒有返回的SQL:
// 返回影響行數(shù)int row = database.executeUpdate("DELETE FROM user_info WHERE id = ?",123);
實體的增刪改
新增:
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)我們在使用ORM時,我們常常需要先編寫查詢列名及表名的SQL語句SELECT * FROM user_info
,其實是非常影響開發(fā)效率的。
因此,PetaPojo
增加了自動添加查詢列與表名
的自動匹配功能。
例如:
UserInfo userInfo = database.firstOrDefault(UserInfo.class, "SELECT * FROM user_info WHERE id = ?", 1);
PetaPojo
可以允許簡化為:
UserInfo userInfo = database.firstOrDefault(UserInfo.class,"WHERE id = ?",1);
查詢組裝器 Sql Builder
在我們查詢數(shù)據(jù)庫時,經(jīng)常需要添加一些條件或排序之類的。總之,盡可能地讓SQL語句動態(tài)化或更靈活,以應(yīng)對復(fù)雜的業(yè)務(wù)需要。
與此同時,如果我們只是進(jìn)行單純的SQL硬編寫,開發(fā)效率將會是一個很大的問題,維護(hù)亦比較復(fù)雜費時。
在此基礎(chǔ)上,PetaPojo
提供了一個非常便捷的SQL查詢組裝器。
基礎(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ù)不同的條件來進(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ù)列表是無限的,只需要與SQL語句中的參數(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);
基于自動添加查詢列
的功能,上述語句可變改為:
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ù)雜的查詢中,我們可這樣用:
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);
枚舉支持
在開發(fā)中,經(jīng)常會碰到一些需要使用枚舉的地方,如訂單狀態(tài),用戶類型等。
JAVA默認(rèn)枚舉類型不是很好用,最主要的問題在于:用戶類型,訂單狀態(tài)這些枚舉在系統(tǒng)中我們可以理解為一個鍵值對的列表,而JAVA默認(rèn)枚舉的index是不可自定義的。
因此,PetaPojo
在枚舉的支持上做了一些擴(kuò)展。
首先,PetaPojo
定義了一個枚舉接口:
/** * 枚舉必須要實現(xiàn)的接口 */public interface IEnumMessage { int getValue(); String getName(); }
并提供了一個枚舉幫助類:
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以及枚舉類型,得到具體的枚舉對象 */ public static <T extends IEnumMessage> T getEnum(Class<T> type, int value) { return (T) getEnumValues(type).get(value); } /** * 將枚舉類型轉(zhuǎn)化為一個鍵值對的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)而,在項目使用枚舉(如UserType
)時,實現(xiàn)IEnumMessage接口即可:
/** * 用戶類型 */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; } }
對應(yīng)的實體:
@TableName("user_info")@PrimaryKey(value = "id",autoIncrement = true)public class UserInfo { private int id; private String userName; private String password; // 直接使用枚舉,數(shù)據(jù)庫中作為int型進(jìn)行存儲 private UserType userType; // ... getter setter}
UserInfo userInfo = database.firstOrDefault(UserInfo.class, "SELECT * FROM user_info WHERE id = ?",1);// 設(shè)置用戶類型為碼農(nóng)userInfo.setUserType(UserType.Coder); database.update(userInfo);
時間類型支持 org.joda.time.DateTime
PetaPojo
支持org.joda.time.DateTime
類型,映射的數(shù)據(jù)庫表字段類型為datetime
:
@TableName("user_info")@PrimaryKey(value = "id",autoIncrement = true)public class UserInfo { private int id; private String userName; private String password; // 直接使用枚舉,數(shù)據(jù)庫中作為int型進(jìn)行存儲 private UserType userType; private DateTime createDate; // ... getter setter}
當(dāng)SQL查詢中的參數(shù)值為org.joda.time.DateTime
時,這樣使用:
Sql sql = Sql.create() .where("createDate >= ?",DateTime.now().toString());// 將DateTime類型toString即可
以上便是整體PetaPojo
的主要功能。