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(); u(10); u("Hello"); u(true); //插入数据 long id=D.M(user).add(); //查询数据 user=D.M).find(id); //更新数据 u(false); D.M(user).field("sex").save();//不指定字段名默认更新JavaBean的所有非空属性 //删除数据 D.M(user).delete(); ).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).find(id); //删除数据 D.M("user").delete(id);项目主页
(码云) (Github)
测试项目
_Demo
2 使用方法
0x01 添加依赖
导入Jar包
引入T核心库和以下两个依赖库:
- my
- common
或者
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 增加功能】
jdbcUrl = jdbc:mysql://127.0.0.1:3306/thinkjdbc?useUnicode=true&characterEncoding=UTF-8 da = root da = 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(";); HikariDataSource dataSource = new HikariDataSource(config); D.setDataSource(dataSource);注:如果定义了数据库连接池,ThinkJD会优先使用。
(3)配置表前缀
只需调用一次,配置表前缀不是必需的
D.setTablePrefix("jd_"); ('user') D.M)将会操作 `jd_user` 表注:D.M('user').prefix('jd_')方法可单独指定表前缀【V1.2.3新增】
0x03 连贯操作
操作参数示例说明tabletable(String table)table("user") pkpk(String key)pk("id") autoIncautoInc(boolean isPkAutoInc)autoInc(false) joinjoin(String join)join("left join machine on u and machine_status=1") fieldfield(String filed)field("id,username") datadata(Object... dataParam)data(11,"Leytton") setIncsetInc(String key,long num)setInc("gold",5) //gold=gold+5 setDecsetDec(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") groupgroup(String group)group("type") havinghaving(String having)having("id>1234") orderorder(String order)order("id desc") ascasc(String key)asc("id") descdesc(String key)desc("id") pagepage(long page, long rows)page(1,10) limit①limit(long rows)
②limit(long offset, long rows)①limit(10)
②limit(1,10) unionunion(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) maxdouble max(String field) mindouble min(String field) avgdouble avg(String field) sumdouble sum(String field)
//find查询 //select id,name from jd_user where id>4 order by id asc limit 0,1 User res = D.M).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).find(3); //find根据字段查询 //select * from jd_user where name='Tom' limit 0,1 User user=D.M).fetchSql(true).find("name","Bob"); //where,field过滤 //select id,name,weight from jd_user where id>3 List<User> res = D.M).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).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_u(gold) as num from jd_user left join jd_gold on user_id=jd_u where jd_u>3 group by jd_u res = D.M).field("jd_u(gold) as num").join("left join jd_gold on user_id=jd_u").where("jd_u>3").group("jd_u").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).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).where("id>3").count(); num= D.M).fetchSql(true).where("id>3").count("id"); num= (long) D.M).fetchSql(false).where("id<0").max("id"); num= (long) D.M).where("id<3").max("id"); num= (long) D.M).min("id"); num= (long) D.M).where("id>3").min("id"); num= (long) D.M).fetchSql(false).where("id>3").avg("id"); double avg= D.M).fetchSql(false).where("id>3").avg("id"); num= (long) D.M).where("id>13441").sum("age");通过调用fetchSql(true)方法,可以获取到 ThinkJD产生的SQL语句(Exception形式)并且不会执行数据库操作。
user表结构:
字段名数据类型备注idint用户id,自增长主键namevarchar用户名agetinyint年龄weightfloat体重sextinyint性别 0女/1男timeint时间
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) { = id; } public Integer getAge() { return age; } public void setAge(Integer age) { = age; } public String getName() { return name; } public void setName(String name) { = name; } public Float getWeight() { return weight; } public void setWeight(Float weight) { = weight; } public Boolean getSex() { return sex; } public void setSex(Boolean sex) { = sex; } public Integer getNum() { return num; } public void setNum(Integer num) { = num; } public Long getTime() { return time; } public void setTime(Long time) { = time; } }0x05 插入数据
操作参数说明addlong add()Table模式前提方法:data()
返回自动生成的主键值;
/*指定插入字段*/ long id=D.M).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).data("Tom",60,...).add(); //使用javaBean全自动模式,自动获取表名、主键、字段名和数据 User user = new User(); u(5); u(10); u("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 更新数据
操作参数说明savelong save()Table模式前提方法:data(),where();
返回执行生效行数
long num=D.M("user").field("name,weight").data("Mike",100).where("id=?",1234).save(); User user = new User(); u(5); u(10); u("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=?",u()-1).save();0x07 删除数据
操作参数说明deletelong delete()Table模式前提方法:where()
返回执行生效行数
注:为防止误删除,where条件不能为空。
long num=D.M("user").delete(5);//默认为id=? num=D.M("user").delete("time",1523681398);//time=? num=D.M).where("id>=?",13421).delete(); //JavaBean模式 User user=new User(); u(10L); long num=D.M(user).delete();0x08 执行SQL
操作参数说明executevoid 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,Sy()/1000).add(); Sy(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,Sy()/1000).add(); Sy(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也是如此。