1 簡介
ThinkJD
,又名ThinkJDBC
,一個簡潔而強大的開源JDBC操作庫。你可以使用Java像ThinkPHP
框架的M方法一樣,一行代碼搞定數據庫操作
。ThinkJD會自動管理數據庫連接,默認使用完畢或程序異常都會關閉連接以免造成內存溢出。也可以設置手動關閉以複用Connection連接,無需傳入連接實例參數,ThinkJD內部已做多線程安全處理,詳見【0x0A 多線程安全】
。
特性
- 核心jar包只有30多KB
- 支持複雜SQL語句以及直接執行SQL語句
- 自動管理數據庫連接
- 支持增刪改查ORM映射
- 支持字段註解映射
- 主鍵、自增、是否ORM字段等屬性配置
- 動態設置>註解設置>全局設置
- 數據庫連接多線程安全
- 支持數據庫連接池
- 簡易的事務操作
- 支持調試模式,輸出SQL語句
- 基於DBUtils,Apache出品,底層質量有保障
先睹爲快:
//數據庫配置(只需調用一次)
D.setDbConfig("jdbc:mysql://127.0.0.1:3306/DbName?characterEncoding=UTF-8","root","root");
//JavaBean模式,自動獲取表名、主鍵、自增屬性、字段名和數據
User user = new User();
user.setAge(10);
user.setName("Hello");
user.setSex(true);
//插入數據
long id=D.M(user).add();
//查詢數據
user=D.M(User.class).find(id);
//更新數據
user.setSex(false);
D.M(user).field("sex").save();//不指定字段名默認更新JavaBean的所有非空屬性
//刪除數據
D.M(user).delete();
//D.M(User.class).delete(id);
//Table模式,手動指定表名、主鍵、自增屬性、字段名和數據
//插入數據
long id=D.M("user").field("name,weight").data("Tom",60).add();
//更新數據
D.M("user").field("name,weight").data("Tom",100).where("id=?",id).save();
//查詢數據
user=D.M(User.class).find(id);
//刪除數據
D.M("user").delete(id);
項目主頁
https://gitee.com/Leytton/ThinkJD (碼雲) https://github.com/Leytton/ThinkJD (Github)
測試項目
https://github.com/Leytton/ThinkJD_Demo
2 使用方法
0x01 添加依賴
導入Jar包
ThinkJDBC-x.x.x-full.jar 包含了ThinkJDBC-x.x.x-core.jar核心庫和兩個依賴庫,只需要添加這一個jar包就行了
- mysql-connector-java-5.1.39.jar
- commons-dbutils-1.6.jar
或者
Maven
<dependency>
<groupId>com.llqqww</groupId>
<artifactId>thinkjdbc</artifactId>
<version>1.4.4_13</version>
</dependency>
0x02 定義數據庫
ThinkJD支持直接定義用戶名密碼訪問數據庫,也支持使用Hikari、C3P0等數據庫連接池。
數據庫連接方式有三種:
(1)配置文件方式
在項目根目錄下添加文件(跟Hikari配置文件格式一樣)
程序第一次啓動時會自動加載讀取配置文件,如果文件不存在則忽略。【V1.2.4_5 增加功能】
thinkjdbc.properties
jdbcUrl = jdbc:mysql://127.0.0.1:3306/thinkjdbc?useUnicode=true&characterEncoding=UTF-8
dataSource.user = root
dataSource.password = root
(2)帳號密碼方式
D.setDbConfig("jdbc:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=UTF-8","root","root");
(3)使用數據庫連接池
例如使用Hikari連接池:
HikariConfig config = new HikariConfig("/hikari.properties");
HikariDataSource dataSource = new HikariDataSource(config);
D.setDataSource(dataSource);
注:如果定義了數據庫連接池,ThinkJD會優先使用。
(3)配置表前綴
只需調用一次,配置表前綴不是必需的
D.setTablePrefix("jd_");
//D.M('user') D.M(User.class)將會操作 `jd_user` 表
注:
D.M('user').prefix('jd_')
方法可單獨指定表前綴【V1.2.3新增】
0x03 連貫操作
操作 | 參數 | 示例 | 說明 |
---|---|---|---|
table |
table(String table) | table(“user”) | |
pk |
pk(String key) | pk(“id”) | |
autoInc |
autoInc(boolean isPkAutoInc) | autoInc(false) | |
join |
join(String join) | join(“left join machine on user.id=user_id and machine_status=1”) | |
field |
field(String filed) | field(“id,username”) | |
data |
data(Object… dataParam) | data(11,”Leytton”) | |
setInc |
setInc(String key,long num) | setInc(“gold”,5) //gold=gold+5 | |
setDec |
setDec(String key,long num) | setDec(“gold”,5) //gold=gold-5 | |
where |
①where(String where) ②where(String where, Object… whereParam) |
①where(“id=1111 and username=’Leytton’”) ②where(“id=? and username=?”,1111,”Leytton”) |
|
group |
group(String group) | group(“type”) | |
having |
having(String having) | having(“id>1234”) | |
order |
order(String order) | order(“id desc”) | |
asc |
asc(String key) | asc(“id”) | |
desc |
desc(String key) | desc(“id”) | |
page |
page(long page, long rows) | page(1,10) | |
limit |
①limit(long rows) ②limit(long offset, long rows) |
①limit(10) ②limit(1,10) |
|
union |
union(String union,Boolean isAll) | ①union(“select * from user_two where id>1234”,false) ②union(“select * from user_two where id>1234”,true) |
0x04 查詢數據
操作 | 參數 | 說明 |
---|---|---|
select | ①<T > List<T > select()②< T > List<T > select(String key, Object value) |
|
find | ①<T > T find()②< T > T find(Object value)③< T > T find(String key, Object value) |
|
count | ①long count() ②long count(String field) |
|
max | double max(String field) | |
min | double min(String field) | |
avg | double avg(String field) | |
sum | double sum(String field) |
//find查詢
//select id,name from jd_user where id>4 order by id asc limit 0,1
User res = D.M(User.class).field("id,name").where("id>?",4).order("id asc").find();
//find 根據id查詢
//select * from jd_user where id=3 limit 0,1
User user = D.M(User.class).find(3);
//find根據字段查詢
//select * from jd_user where name='Tom' limit 0,1
User user=D.M(User.class).fetchSql(true).find("name","Bob");
//where,field過濾
//select id,name,weight from jd_user where id>3
List<User> res = D.M(User.class).field("id,name,weight").where("id>3").select();
//group分組查詢
//select sex,sum(weight) as weight,avg(age) as age,count(id) as num from jd_user where id>5 group by sex order by sex desc limit 0,10
res = D.M(User.class).field("sex,sum(weight) as weight,avg(age) as age,count(id) as num").where("id>?",5).group("sex").order("sex desc").page(1, 10).select();
//join聯表查詢
//select jd_user.id,name,weight,sum(gold) as num from jd_user left join jd_gold on user_id=jd_user.id where jd_user.id>3 group by jd_user.id
res = D.M(User.class).field("jd_user.id,name,weight,sum(gold) as num").join("left join jd_gold on user_id=jd_user.id").where("jd_user.id>3").group("jd_user.id").select();
//union聯表查詢
//(select id,name from jd_user where id=4 ) union all (select id,name from jd_user where id<3) union (select id,name from jd_user where id=3)
res = D.M(User.class).field("id,name").where("id=4").union("select id,name from jd_user where id<3",true)
.union("select id,name from jd_user where id=3",false).select();
//統計查詢
long num= new M(User.class).where("id>3").count();
num= D.M(User.class).fetchSql(true).where("id>3").count("id");
num= (long) D.M(User.class).fetchSql(false).where("id<0").max("id");
num= (long) D.M(User.class).where("id<3").max("id");
num= (long) D.M(User.class).min("id");
num= (long) D.M(User.class).where("id>3").min("id");
num= (long) D.M(User.class).fetchSql(false).where("id>3").avg("id");
double avg= D.M(User.class).fetchSql(false).where("id>3").avg("id");
num= (long) D.M(User.class).where("id>13441").sum("age");
通過調用
fetchSql(true)
方法,可以獲取到ThinkJD
產生的SQL語句(Exception形式)並且不會執行數據庫操作。
user表結構:
字段名 | 數據類型 | 備註 |
---|---|---|
id | int | 用戶id,自增長主鍵 |
name | varchar | 用戶名 |
age | tinyint | 年齡 |
weight | float | 體重 |
sex | tinyint | 性別 0女/1男 |
time | int | 時間 |
select()
和 find()
查詢結果封裝到JavaBean裏返回,JavaBean可使用註解映射數據庫字段。
注意:牆裂建議JavaBean字段基礎數據類型使用【Integer、Long、Boolean、Float、Double、Byte、Short、Char】
不要使用【integer、long、boolean、float、double、byte、short、char】,因爲前者可以賦值爲null而後者不行
(null時爲0),所以獲取到的值是不準確的。ThinkJD的save更新等操作通過判斷屬性值不爲null則加入數據庫更新
字段隊列。ThinkJD會自動檢測以上不符合的數據類型併發出警告。如需關閉調用D.setCheckField(false);
//@Table(name="user")默認類名爲表名,可註解重定義
public class User {
//@Column(isKey=true)默認id爲主鍵、isAutoInc=true自增,可註解重定義
private Long id;
private Integer age;
//@Column(name="user_name")默認屬性名爲表字段,可註解重定義
private String name;
private Float weight;
private Boolean sex;
@Column(isColumn=false)
private Integer num;
private Long time;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getWeight() {
return weight;
}
public void setWeight(Float weight) {
this.weight = weight;
}
public Boolean getSex() {
return sex;
}
public void setSex(Boolean sex) {
this.sex = sex;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public Long getTime() {
return time;
}
public void setTime(Long time) {
this.time = time;
}
}
0x05 插入數據
操作 | 參數 | 說明 |
---|---|---|
add | long add() | Table模式前提方法:data() 返回自動生成的主鍵值; |
/*指定插入字段*/
long id=D.M(User.class).field("name,weight").data("Tom",60).add();
/*不指定插入字段,按表字段順序插入*/
id=D.M("user").data(null,"Tom",60,...).add();
/*使用javaBean半自動模式,自動獲取表名、主鍵、字段名,給定data按javaBean屬性順序插入,生成的sql語句如下
*insert into jd_user (age,name,weight,sex,time) values(?,?,?,...)
*/
id=D.M(User.class).data("Tom",60,...).add();
//使用javaBean全自動模式,自動獲取表名、主鍵、字段名和數據
User user = new User();
user.setId(5);
user.setAge(10);
user.setName("Hello");
//insert into jd_user (age,name) values(?,?) Params[10,Hello]
num=D.M(user).add();
//insert into jd_user (name) values(?) Params[Hello]
num=D.M(user).field("name").add();
//insert into jd_user (id,age,name) values(?,?,?) Params[5,10,Hello]
num=D.M(user).autoInc(false).add();
0x06 更新數據
操作 | 參數 | 說明 |
---|---|---|
save | long save() | Table模式前提方法:data(),where(); 返回執行生效行數 |
long num=D.M("user").field("name,weight").data("Mike",100).where("id=?",1234).save();
User user = new User();
user.setId(5);
user.setAge(10);
user.setName("Hello");
//update jd_user set age=?,name=? where id=?; Params[10,Hello,5]
num=D.M(user).save();
//update jd_user set name=? where id=?; Params[Hello,5]
num=D.M(user).field("name").save();
//update jd_user set id=?,age=?,name=? where id=?; Params[5,10,Hello,4]
id=D.M(user).autoInc(false).fetchSql(true).where("id=?",user.getId()-1).save();
0x07 刪除數據
操作 | 參數 | 說明 |
---|---|---|
delete | long delete() | Table模式前提方法:where() 返回執行生效行數 |
注:爲防止誤刪除,where條件不能爲空。
long num=D.M("user").delete(5);//默認爲id=?
num=D.M("user").delete("time",1523681398);//time=?
num=D.M(User.class).where("id>=?",13421).delete();
//JavaBean模式
User user=new User();
user.setId(10L);
long num=D.M(user).delete();
0x08 執行SQL
操作 | 參數 | 說明 |
---|---|---|
execute | void execute(String… sqls) | 直接執行SQL語句 |
D.M().execute( sql1 [ sql2 , sql3 ... ] );
0x09 事務支持
數據庫表引擎應該爲InnoDB以支持事務操作。
代碼示例:
Connection conn=null;
try {
//獲取已開啓事務的數據庫連接
conn = D.M().startTrans();
//使用事務連接操作數據庫
long id=new M("gold").trans(conn).field("user_id,gold,type,time").data(3,5,0,System.currentTimeMillis()/1000).add();
System.out.println(id);
if(id>0) {
throw new SQLException("Transaction Rollback Test");
}
id=new M("gold").trans(conn).field("user_id,gold,type,time").data(3,5,0,System.currentTimeMillis()/1000).add();
System.out.println(id);
//提交事務
D.M().commit(conn);
} catch (SQLException e) {
e.printStackTrace();
try {
//事務回滾
D.M().rollback(conn);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
0x0A 多線程安全
【V1.4.4_12功能】
/*設置數據庫操作完畢後不自動關閉
*此處是爲了提高數據庫操作性能,不用頻繁地獲取和關閉連接,同一線程內ThinkJD會使用同一連接;
*默認自動關閉連接的話,每次操作都會獲取一個新的Connection,使用完畢立即自動關閉
*/
D.setAutoClose(false);
new Thread(new Runnable() {
@Override
public void run() {
Gold gold = new Gold();
gold.setUser_id(1L);
gold.setGold(5);
gold.setGold_type(0);
try {
D.M(gold).add();
D.M(gold).add();
} catch (SQLException e) {
D.closeConn();
e.printStackTrace();
}
}
}, "Thread_1").start();
new Thread(new Runnable() {
@Override
public void run() {
Gold gold = new Gold();
gold.setUser_id(2L);
gold.setGold(5);
gold.setGold_type(0);
try {
D.M(gold).add();
D.M(gold).add();
} catch (SQLException e) {
D.closeConn();
e.printStackTrace();
}
}
}, "Thread_2").start();
獲取數據庫連接處輸出日誌爲:
Thread:Thread_1,conn==null:true
Thread:Thread_2,conn==null:true
Thread:Thread_2,conn==null:false
Thread:Thread_1,conn==null:false
由此可見,線程1第一次操作數據庫時conn爲空,會獲取一個新的conn,下次操作時conn不爲空可以直接使用,直到調用D.closeConn();
後conn纔會關閉。線程2也是如此。
3 許可證
4 關於
如果喜歡的話,請點個贊讓我知道哦~在找到比它用得更順手的JDBC庫之前,這個項目會持續更新。