jdbc是一种由一组用Java语言编写的标准类和标准接口组成,用于执行SQL语句的Java API。JDBC提供了面向多种关系型数据库连接的统一访问,同时统一规范了标准接口和工具,使数据库开发人员能够编写数据库应用程序,实现了所有这些面向标准的目标并且具有简单,严格类型定义且高性能实现的接口。
如下图,我们很明显的看出:java应用程序是不能直接访问关系型数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。而所谓的数据库驱动,实际上是java中抽象工厂的一个具体实例。其实也就是通过JDBC API接口中的抽象工厂来生产各种关系数据库的抽象接口数据库驱动厂商JDBC DriverManager(对Connection等接口的实现类的jar文件),来分别具体生产实现各种关系型数据库的驱动接口,从而使得程序员可向相应数据库发送SQL调用。同时,将Java语言和JDBC结合起来使程序员不必为不同的平台编写不同的应用程序,只须写一遍程序就可以让它在任何平台上运行,这也是Java语言“编写一次,处处运行”的优势。
在Java程序中,连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。我们直接使用Driver接口(由数据库厂家提供),可以通过 “Class.forName(“指定数据库的驱动程序”)” 方式来加载添加到开发环境中的驱动程序,例如:
装载MySql驱动:Class.forName(“com.mysql.jdbc.Driver”);
装载Oracle驱动:Class.forName(“oracle.jdbc.driver.OracleDriver”);
实际上,JDBC 源码中第一句 的 class.forName(),做了很多的事情, 在jdk 中,只有Driver 的一个接口,没有一个具体实现实例,就是说mysql-connector-java.jar 这种类似的 jar 需要第三方去实现,具体的Driver 实现类是在第三方的jar里面的。那么它是怎么实现的呢?在 class.forName 里面,指定加载 第三方的 Driver接口,那么在类加载的时候,可以完成 jdk 中 Driver 接口, 在 第三方jar 中具体实现了class(第三方 的jar里面,存在一个 配置文件,指向了在 第三方 jar 中具体实现了 jdk 中 sql 包下的 Driver 接口的 类),同时Driver 接口中使用了泛型,这样就可以通过反射的方式获取到 第三方中具体实现类,同时解析SQL语句不同类型的结果集,映射为不同的实体类型。
本文最后,本人写了一个小型配置文件的BaseDao,来封装实现了mysql的SQL语句在java中的实现细节,仅供参考,来理解JDBC具体实现的原理。
char/varchar/text java.lang.String
tinyint/smallint/int/bigint java.lang.Byte/Short/Integer/Long
decimal java.math.BigDecimal
bit java.lang.Boolean
datetime/timestamp/date java.sql.Date
加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement → 处理执行结果ResultSet → 释放资源
下面以java连接mysql数据库为例:
Class.forName("com.mysql.jdbc.Driver");
原则上必须只装载一次。
final String URL = "jdbc:mysql://192.168.40.103:3306/school?useUnicode=true&characterEncoding=utf-8&useSSL=true"
,USERNAME = "root",
PASSWORD = "kb08";
Connection con = DriverManager.getConnection(URL,USERNAME,PASSWORD);
建立连接对象Connection的传入参数必须是常量,事实上,真正的连接对象过程都是写在配置文件里的,所以,当然是常量。
关于URL的结构如下:
协议:子协议://主机IP:端口号/数据库?【参数名:参数值】【其他参数如:统一编码&字符编码格式&加密套接字协议】
备注:【】为可选项,不写,就是默认值。其他为必选项,且顺序必须一致。
这里使用的是PreparedStatement预编译SQL语句执行,关于Statement执行SQL语句执行(不实用,且有SQL注入风险)和prepareCall执行自定义函数和存储过程(不常用),这里不做举例了。
//创建执行对象
Statement sta = con.createStatement();
//执行sql命令
/*非查询操作*/
final String SQL1 = "insert into deptinfo(deptName) value('市场部')";
final String SQL2 = "create table deptinfo";
int rst1 = sta.executeUpdate(SQL1);
int rst2 = sta.executeUpdate(SQL2);
System.out.println(rst1);//结果1,DML语句成功执行,返回1
System.out.println(rst2);//结果0,DDL语句成功执行,返回0
/*查询操作*/
//返回结果集
final String SQL3 = "select * from deptinfo";
ResultSet rst3 = sta.executeQuery(SQL);
while(rst3.next()){
System.out.print(rst3.getInt(1)+"\t");
System.out.println(rst3.getString("deptName"));
}
//占位符的使用
final String SQL = "select * from empinfo where deptId=?";
PreparedStatement pst = con.prepareStatement(SQL);
pst.setObject(1,3);
ResultSet rst = pst.executeQuery();
ResultSetMetaData struct = rst.getMetaData();
String[] arr = new String[struct.getColumnCount()];
while (rst.next()) {
for (int i = 1; i <= arr.length; i++) {
arr[i - 1] = rst.getObject(i).toString();
}
System.out.println(MessageFormat.format("{0}\t{1}\t{2}\t{3}", arr));
}
占位符,其实非常像JQuery中的模板字符串的形式,同时也可以理解为java中String的Message.format()的填坑操作。
PoolUtil.close(rst);
PoolUtil.close(pst);
PoolUtil.close(con);
这里的释放资源我调用了我自己封装的关闭资源的工具类PoolUtil进行释放资源。
BaseDao,就是数据库访问对象的工具类,它广泛应用于mysql通过JDBC在java平台中SQL语句的执行,它对java执行SQL语句的过程进行了封装,方便多个数据库连接对象同时多并发地去调用该工具类去执行多条SQL语句(包含基本的增删改查、自定义函数和存储过程的执行),是一款功能强大的工具类。本人搭建了一个简单的BaseDao,供大家参考。
1、查询操作:通过自定义类型SelRtn反馈【是否存在异常】和查询结果
2、BaseDao对象的管理缺失(不停的创建):单例工厂的模式
3、连接字符串管理:通过properties小型配置文件
4、并发管理:通过弹性线程池ConPool对多并发和Connection对象进行管理
#mysql配置
#mysql 驱动装载配置
mysql01.driver=com.mysql.jdbc.Driver
#mysql url配置(数据库地址)
mysql01.url=jdbc:mysql://192.168.40.103:3306/school?useUnicode=true&characterEncoding=utf-8&userSSL=true
#mysql 连接对象用户名
mysql01.username=root
#mysql 连接对象用户密码
mysql01.password=kb08
#连接池配置
#核心连接池数
pool.coreCount=3
#最大连接池数
pool.maxCount=10
#最大等待时间(s)
pool.maxWait=5
#连接池最大空闲时间(s)
pool.maxIdle=30
#重新尝试连接间隔时间(ms)
pool.retryInterval=50
#重新尝试连接最大次数
pool.maxRetryCount=8
#连接池异常信息 0:无异常 1:异常
pool.exitOnErr=0
package cn.kgc.kb08.jdbc.dao3.impl;
public interface PoolConstant {
String POOL_CORE_COUNT = "coreCount";
String POOL_MAX_COUNT = "maxCount";
String POOL_MAX_WAIT = "maxWait";
String POOL_MAX_IDLE = "maxIdle";
String POOL_RETRY_INTERVAL = "retryInterval";
String POOL_MAX_RETRY_COUNT = "maxRetryCount";
String POOL_EXIT_ON_ERR = "exitOnErr";
String[] POOL = {
POOL_CORE_COUNT,
POOL_MAX_COUNT,
POOL_MAX_WAIT,
POOL_MAX_IDLE,
POOL_RETRY_INTERVAL,
POOL_MAX_RETRY_COUNT,
POOL_EXIT_ON_ERR
};
String MYSQL_DRI = "driver";
String MYSQL_URL = "url";
String MYSQL_USER = "username";
String MYSQL_PASS = "password";
String[] MYSQL = {
MYSQL_DRI,
MYSQL_URL,
MYSQL_USER,
MYSQL_PASS
};
}
package cn.kgc.kb08.jdbc.dao3;
public interface Pool {
void destroy();
Dao newDao();
}
package cn.kgc.kb08.jdbc.dao3;
import cn.kgc.kb08.jdbc.dao3.impl.ConPool;
/**
* 连接池的单例模式
*/
public abstract class PoolFactory {
private static Pool pool;
private static synchronized void init(){
if (null==pool){
pool = new ConPool();
}
}
public static Pool get(){
if (null==pool){
init();
}
return pool;
}
}
package cn.kgc.kb08.jdbc.dao3.impl;
import cn.kgc.kb08.jdbc.dao3.Pool;
import cn.kgc.kb08.jdbc.dao3.SelRtn;
import cn.kgc.kb08.jdbc.dao3.Dao;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static cn.kgc.kb08.jdbc.dao3.impl.PoolUtil.close;
/**
* Base:通用
* Dao:DataBase Access Object 数据库访问对象
* @version kb08-3.0
* @since kb08-1.0
* 已解决缺陷:
* 1、查询操作:通过自定义类型SelRtn反馈【是否存在异常】和查询结果
* 2、BaseDao对象的管理缺失(不停的创建):单例工厂的模式
* 3、连接字符串管理:通过properties小型配置文件
* 4、并发管理:通过弹性线程池PoolUtil对多并发和Connection对象进行管理
*/
public final class ConPool implements Pool {
/**
* 自定义类型:池中连接
*/
private class PoolCon{
//池连接对象是否空闲
boolean free = true;
//池连接对象是否为核心连接对象
boolean core;
//连接数据库对象
Connection con;
//临时池连接对象空闲开始时间
Long idleBegin;
public PoolCon(boolean core,Connection con){
this.core = core;
this.con = con;
resetIdle();
}
public void resetIdle(){
if (!core){
this.idleBegin = System.currentTimeMillis();
}
}
}
//池连接键值对
private ConcurrentMap<Integer,PoolCon> pool;
//池连接配置信息键值对
private Map<String,Integer> cnfPool;
//数据库连接配置信息键值对
private Map<String,String> cnfCon;
/**
* 执行定期清理线程池
* 1、检查核心连接对象的有效性:无效则创建新核心连接对象覆盖之
* 2、检查临时连接对象是否超时,超时则并关闭并移除
*/
private ScheduledExecutorService schedule;
//主线程池(后面定义为配置信息的2倍数量)
private ExecutorService service;
private Lock lock;
private Condition cond;
//临时池连接对象是否正在清理
private boolean clearing;
//数据库访问对象
private Dao dao;
public ConPool(){
initCnf();
initPool();
startClear();
}
/**
* 初始化池和数据配置信息
*/
private void initCnf(){
cnfPool = PoolUtil.parse(Integer.class,"pool",
Arrays.asList(PoolConstant.POOL));
cnfCon = PoolUtil.parse(String.class,"mysql01",
Arrays.asList(PoolConstant.MYSQL));
}
/**
* 初始化连接池(核心连接对象)
*/
private void initPool(){
final int MAX_COUNT = cnfPool.get(PoolConstant.POOL_MAX_COUNT);
//主线程池数量为配置信息最大数量的2倍
service = Executors.newFixedThreadPool(MAX_COUNT*2);
schedule = Executors.newSingleThreadScheduledExecutor();
lock = new ReentrantLock(true);
cond = lock.newCondition();
//支持并发操作的Map
pool = new ConcurrentHashMap<>(MAX_COUNT);
PoolCon pc;
final int CORE_COUNT = cnfPool.get(PoolConstant.POOL_CORE_COUNT);
//初始化核心池连接对象
//变量j的作用是保证核心连接池对象创建成功(数量与配置信息一致),即使线程中途中断,也能保证成功
for (int i = 1,j = 1; i <= CORE_COUNT; i++) {
pc = mkPoolCon(true);
if (null!=pc){
System.out.println(j+"池连接对象创建成功");
pool.put(j++,pc);
}
}
if (pool.size()==0){
System.out.println("连接池初始化失败,系统强制退出");
System.exit(-1);
}
//规则:连接对象不小于最大连接对象的一半,或者出现异常(配置信息的异常为1)
if (cnfPool.get(PoolConstant.POOL_EXIT_ON_ERR)==1 && pool.size()<=CORE_COUNT/2){
System.out.println("连接池初始化有效核心连接数为未过半异常,系统强制退出");
System.exit(-1);
}
System.out.println("连接池初始化成功");
}
/**
* 创建一个池对象
* @param core 池对象类型:true:核心对象 false:临时对象
* @return PoolCon
*/
private PoolCon mkPoolCon(boolean core){
PoolCon pc = null;
for (int i = 1; i <=cnfPool.get(PoolConstant.POOL_MAX_RETRY_COUNT); i++) {
try {
//建立与数据库的连接
Connection con = DriverManager.getConnection(
cnfCon.get(PoolConstant.MYSQL_URL),
cnfCon.get(PoolConstant.MYSQL_USER),
cnfCon.get(PoolConstant.MYSQL_PASS)
);
pc = new PoolCon(core,con);
break;
} catch (SQLException e) {
try {
//尝试配置信息的间隔时间进行请求建立池连接对象
TimeUnit.SECONDS.sleep(cnfPool.get(PoolConstant.POOL_RETRY_INTERVAL));
continue;
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
}
}
}
return pc;
}
/**
* 验证连接对象是否有效
* @param pc 池连接对象
* @return true :有效 false:无效
*/
private boolean isPCValid(PoolCon pc){
try {
//执行一条无意义的SQL语句
pc.con.createStatement().executeQuery("select 1");
return true;
} catch (SQLException e) {
return false;
}
}
/**
* 验证临时连接对象是否过期
* @param pc 池连接对象
* @return true: 过期 false:未过期
*/
private boolean isExpired(PoolCon pc){
//临时连接对象空闲时间超过配置信息的最大空闲时间
return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()-pc.idleBegin)
>=cnfPool.get(PoolConstant.POOL_MAX_IDLE);
}
/**
* 验证用户等待是否超时(配置最大时限)
* @param waitBegin 计算参考起点时间
* @return true:等待已超时 false:等待未超时
*/
private boolean isWaitExpried(long waitBegin){
//连接对象请求未获得批准时间超过配置信息的最大请求等待时间
return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()-waitBegin)>=
cnfPool.get(PoolConstant.POOL_MAX_WAIT);
}
/**
* 开启定期清理任务
*/
private void startClear(){
System.out.println("池清理线程启动成功。。。。");
//定时时间为配置信息的最大空闲时间
int delay = cnfPool.get(PoolConstant.POOL_MAX_IDLE);
schedule.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
//定时清理时获得锁,正在清理
lock.lock();
clearing = true;
//非空闲的连接池对象不清理
for (Integer key : pool.keySet()) {
PoolCon pc = pool.get(key);
if (!pc.free){
continue;
}
//核心连接池对象无效,重新建立覆盖之
if (pc.core){
if (!isPCValid(pc)){
pool.put(key,mkPoolCon(true));
}
}else{
//临时连接池对象无效或者已超时,进行清理
if (isExpired(pc) || !isPCValid(pc)){
pool.remove(key);
}
}
}
//定时清理后释放锁,通知其他线程,已完成清理
clearing = false;
cond.signalAll();
lock.unlock();
}
},delay,delay,TimeUnit.SECONDS);
}
/**
* 连接池销毁
*/
@Override
public void destroy(){
//只要有连接池对象就有间隔时间的判断是否销毁
while (pool.size()>0){
for (Integer key : pool.keySet()) {
PoolCon pc = pool.get(key);
//连接池对象空闲方能清理,释放资源,并移除
if (pc.free){
pc.free = false;
close(pc.con);
pool.remove(key);
System.out.println("释放池连接"+key+"对象成功");
}
}
try {
TimeUnit.MILLISECONDS.sleep(cnfPool.get(PoolConstant.POOL_RETRY_INTERVAL));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 从池中获取一个有效连接对象
* @return PoolCon
*/
private PoolCon fetch(){
long waitBegin = System.currentTimeMillis();
for (int i = 1; i <=cnfPool.get(PoolConstant.POOL_MAX_RETRY_COUNT) ; i++) {
try {
//上锁,正在清理就等待
lock.lock();
if (clearing) {
cond.await();
}
for (Integer key : pool.keySet()) {
PoolCon pc = pool.get(key);
//连接池对象空闲且有效方能取出
if (pc.free && isPCValid(pc)){
pc.free = false;
System.out.println("成功取出池连接对象");
return pc;
}
}
//没有超时无法获得,一定时间间隔后再次请求
if (isWaitExpried(waitBegin)){
return null;
}
TimeUnit.MILLISECONDS.sleep(cnfPool.get(PoolConstant.POOL_RETRY_INTERVAL));
}catch (Exception ex){
ex.printStackTrace();
}finally {
lock.unlock();
}
}
//取出临时连接对象(并非是第一个临时连接池对象)
if (pool.size()<cnfPool.get(PoolConstant.POOL_MAX_COUNT)){
PoolCon pc = mkPoolCon(false);
if (null!=pc){
pc.free = false;
pool.put(pool.size()+1,pc);
System.out.println("成功取出池连接对象");
return pc;
}
}
return null;
}
/**
* 归还一个有效连接对象给池中
* @param pc
*/
private void giveBack(PoolCon pc){
//没有连接池对象,返回
if (null==pc){
return;
}
//非核心连接池对象重置空闲时间
if (!pc.core){
pc.resetIdle();
}
pc.free = true;
}
/**
* 初始化Dao对象
*/
private synchronized void init(){
if (null==dao){
dao = new Dao() {
/**
* 自定义方法:获取预编译执行对象
* @param con 连接对象
* @param SQL 数据库操作命令
* @param params 动态参数:预编译参数
* @return 预编译执行对象
* @throws SQLException
*/
private PreparedStatement getPst(Connection con,final String SQL,Object...params) throws SQLException {
PreparedStatement pst = con.prepareStatement(SQL);
if (null!=params && params.length>0){
for (int i = 0; i < params.length; i++) {
pst.setObject(i+1,params[i]);
}
}
return pst;
}
/**
* 自定义方法:执行增删改操作
* @param pst 执行对象
* @return int
* @throws SQLException
*/
private int update(PreparedStatement pst) throws SQLException {
return pst.executeUpdate();
}
/**
* 自定义方法:执行查询操作
* @param pst 执行对象
* @return 结果集
* @throws SQLException
*/
private ResultSet query(PreparedStatement pst) throws SQLException {
return pst.executeQuery();
}
/**
* 自定义方法:反射解析实体参数类型的set方法,并以属性名为键,以方法对象为值进行存储
* @param c 实体类的实际类型
* @return HashMap
*/
private HashMap<String, Method> parseMethod(Class c) throws Exception{
HashMap<String, Method> mapMethod = new HashMap<>();
final String PREFIX = "set";
for (Method method : c.getDeclaredMethods()) {
String name = method.getName();
if (!name.startsWith(PREFIX)){
continue;
}
name = name.substring(3);
name = name.substring(0,1).toLowerCase()+name.substring(1);
mapMethod.put(name,method);
}
return mapMethod;
}
/**
* 自定义方法:解析结果集携带的表结构(表字段名称)
* @param md 结构对象
* @return Stirng[]
* @throws SQLException
*/
private String[] parseStruct(ResultSetMetaData md) throws SQLException {
String[] names = new String[md.getColumnCount()];
for (int i = 0; i < names.length; i++) {
names[i] = md.getColumnLabel(i+1);
}
return names;
}
/**
* 自定义方法:封装数据库的增删改操作
* @param SQL 数据库命令
* @param params 动态参数: 预编译命令参数
* @return int: -1:异常 0:失败 >0表示成功(多条操作)
*/
@Override
public int exeUpd(final String SQL,final Object...params){
try {
return service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int rst = 0;
PoolCon pc = null;
PreparedStatement pst = null;
try {
pc = fetch();
if (null!=pc){
pst = getPst(pc.con,SQL,params);
rst = update(pst);
}
} catch (SQLException e) {
rst = -1;
}finally {
close(pst);
giveBack(pc);
}
return rst;
}
}).get();
} catch (Exception e) {
return -1;
}
}
/**
* 自定义方法:封装查询单个值
* @param 泛型
* @param c 需要单个值的实际类型:基本类型的包装类型
* @param SQL 数据库命令
* @param params 动态参数:预编译参数值
* @return SelRtn: isErr=true:异常 isErr=false(rtn=null:失败,否则:成功)
*/
@Override
public <T> SelRtn exeSingle(final Class<T> c, final String SQL, final Object...params){
try {
return service.submit(new Callable<SelRtn>() {
@Override
public SelRtn call() throws Exception {
PoolCon pc = null;
PreparedStatement pst = null;
ResultSet rst = null;
try {
pc = fetch();
pst = getPst(pc.con,SQL,params);
rst = query(pst);
if (null!=rst && rst.next()){
return SelRtn.succeed(c.getConstructor(String.class).newInstance(rst.getObject(1).toString()));
}else{
return SelRtn.succeed(null);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
PoolUtil.close(rst,pst);
giveBack(pc);
}
return SelRtn.fail();
}
}).get();
} catch (Exception e) {
return SelRtn.fail();
}
}
/**
* 自定义方法:封装查询多行多列
* @param 泛型
* @param c 需要实际类型(实体自定义类型)
* @param SQL 数据库命令
* @param params 动态参数:预编译参数值
* @return SelRtn: isErr=true:异常 isErr=false(rtn=null:失败,否则:成功)
*/
@Override
public <T> cn.kgc.kb08.jdbc.dao3.SelRtn exeQuery(final Class<T> c, final String SQL, final Object...params){
try {
return service.submit(new Callable<SelRtn>() {
@Override
public SelRtn call() throws Exception {
PoolCon pc = null;
PreparedStatement pst = null;
ResultSet rst = null;
try {
pc = fetch();
pst = getPst(pc.con,SQL,params);
rst = query(pst);
if (null!=rst && rst.next()){
List<T> list = new ArrayList<>();
Map<String, Method> map = parseMethod(c);
String[] names = parseStruct(rst.getMetaData());
do{
T t = c.newInstance();
for (String name : names) {
map.get(name).invoke(t,rst.getObject(name));
}
list.add(t);
}while (rst.next());
return SelRtn.succeed(list);
}else{
return SelRtn.succeed(null);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
PoolUtil.close(rst,pst);
giveBack(pc);
}
return SelRtn.fail();
}
}).get();
} catch (Exception e) {
return SelRtn.fail();
}
}
};
}
}
/**
* 实例化Dao对象
* @return Dao
*/
@Override
public Dao newDao(){
if (null==dao){
init();
}
return dao;
}
}
package cn.kgc.kb08.jdbc.dao3.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class PoolUtil {
/**
* 自定义方法:解析数据源配置信息
* @param dataSource 数据源名称
* @return Map
*/
protected static <T> Map<String,T> parse(Class<T> c,String dataSource, List<String> items){
File config = new File("config/sys.properties");
Properties pro = new Properties();
try {
pro.load(new FileInputStream(config));
Map<String,T> map = new HashMap(items.size());
for (String item : items) {
String key = dataSource+"."+item;
if (!pro.containsKey(key)){
throw new IOException("缺失配置项:"+item);
}
map.put(item,c.getConstructor(String.class).newInstance(pro.getProperty(key)));
}
return map;
} catch (Exception e) {
System.out.println(dataSource+"数据源配置信息异常,系统强制退出:"+e.getMessage());
System.exit(-1);
}finally {
if (null!=pro){
//清空键值对,并清空引用
pro.clear();
pro = null;
}
}
return null;
}
/**
* 数据库资源释放
* @param acs 动态参数:待释放资源
*/
protected static void close(AutoCloseable...acs){
for (AutoCloseable ac : acs) {
if (null!=ac){
try {
ac.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
ackage cn.kgc.kb08.jdbc.dao3;
public interface Dao {
int exeUpd(final String SQL, Object... params);
<T> SelRtn exeSingle(final Class<T> c, final String SQL, final Object... params);
<T> cn.kgc.kb08.jdbc.dao3.SelRtn exeQuery(final Class<T> c, final String SQL, final Object... params);
}
package cn.kgc.kb08.jdbc.dao3;
/**
* 完善查询操作返回类型,对于异常的缺失
*/
public final class SelRtn {
private boolean err = false;
private Object rtn;
public static SelRtn succeed(Object rtn){
return new SelRtn(rtn);
}
public static SelRtn fail(){
return new SelRtn();
}
private SelRtn(Object rtn){
this.rtn = rtn;
}
private SelRtn(){
this.err = true;
}
public boolean isErr(){
return this.err;
}
public <T> T getRtn(){
return (T)rtn;
}
}
package cn.kgc.kb08.jdbc.entity;
public class Student {
private Integer id;
private Integer classId;
private String stuName;
private String stuGender;
private String province;
private String city;
private String district;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getClassId() {
return classId;
}
public void setClassId(Integer classId) {
this.classId = classId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public String getStuGender() {
return stuGender;
}
public void setStuGender(Boolean stuGender) {
this.stuGender = stuGender?"男":"女";
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDistrict() {
return district;
}
public void setDistrict(String district) {
this.district = district;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", classId=" + classId +
", stuName='" + stuName + '\'' +
", stuGender='" + stuGender + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
", district='" + district + '\'' +
'}';
}
}
package cn.kgc.kb08.jdbc.daotest;
import cn.kgc.kb08.jdbc.dao3.Pool;
import cn.kgc.kb08.jdbc.dao3.SelRtn;
import cn.kgc.kb08.jdbc.dao3.Dao;
import cn.kgc.kb08.jdbc.dao3.PoolFactory;
import cn.kgc.kb08.jdbc.entity.Student;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
Pool pool = PoolFactory.get();
final Dao dao = pool.newDao();
final int PAGE_SIZE = 10;
String sql = "select ceil(count(1)/?) from studentinfo";
final int TOTAL = dao.exeSingle(Integer.class,sql,PAGE_SIZE).getRtn();
ExecutorService service = Executors.newFixedThreadPool(TOTAL);
final String SQL = "select * from studentinfo limit ?,?";
for (int i = 1; i <= TOTAL; i++) {
final int I = i;
service.submit(new Runnable() {
@Override
public void run() {
SelRtn sr = dao.exeQuery(Student.class, SQL,(I-1)*PAGE_SIZE,PAGE_SIZE);
System.out.println("\n第"+I+"页*******************************");
if (!sr.isErr()){
List<Student> list = sr.getRtn();
if (null!=list&&list.size()>0){
for (Student student : list) {
System.out.println(student);
}
}else{
System.out.println("查无记录");
}
}else{
System.out.println("出错");
}
}
});
}
pool.destroy();
}
}
结果:
1池连接对象创建成功
2池连接对象创建成功
3池连接对象创建成功
连接池初始化成功
池清理线程启动成功。。。。
成功取出池连接对象
释放池连接1对象成功
释放池连接2对象成功
释放池连接3对象成功
成功取出池连接对象
第7页*******************************
Student{id=61, classId=5, stuName='葛优', stuGender='男', province='安徽省', city='蚌埠市', district='五河县'}
Student{id=62, classId=5, stuName='赵伟伟', stuGender='男', province='江苏省', city='南通市', district='海安市'}
Student{id=63, classId=5, stuName='汤伟杰', stuGender='男', province='山东省', city='潍坊市', district='诸城市'}
Student{id=64, classId=5, stuName='江玉', stuGender='男', province='江苏省', city='淮安市', district='盱眙县'}
Student{id=65, classId=5, stuName='戴媛媛', stuGender='女', province='安徽省', city='安庆市', district='桐城市'}
Student{id=66, classId=5, stuName='赵杰', stuGender='男', province='安徽省', city='六安市', district='霍山县'}
Student{id=67, classId=5, stuName='吴悠', stuGender='男', province='天津市', city='市辖区', district='滨海新'}
Student{id=68, classId=5, stuName='孙亚洲', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
Student{id=69, classId=5, stuName='李佛光', stuGender='男', province='江苏省', city='南通市', district='如东县'}
Student{id=70, classId=5, stuName='王家能', stuGender='男', province='云南省', city='楚雄彝', district='族自治'}
成功取出池连接对象
第5页*******************************
Student{id=41, classId=3, stuName='何健', stuGender='男', province='云南省', city='楚雄彝', district='族自治'}
Student{id=42, classId=3, stuName='唐明汉', stuGender='男', province='江西省', city='抚州市', district='乐安县'}
Student{id=43, classId=3, stuName='郑朋', stuGender='男', province='江苏省', city='泰州市', district='泰兴市'}
Student{id=44, classId=3, stuName='陈立志', stuGender='男', province='安徽省', city='亳州市', district='涡阳县'}
Student{id=45, classId=3, stuName='庄俊伟', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
Student{id=46, classId=3, stuName='戴彬', stuGender='男', province='上海市', city='市辖区', district='浦东新'}
Student{id=47, classId=4, stuName='陈元生', stuGender='男', province='江苏省', city='泰州市', district='泰兴市'}
Student{id=48, classId=4, stuName='张刘留', stuGender='男', province='江苏省', city='南京市', district='高淳区'}
Student{id=49, classId=4, stuName='刘青青', stuGender='女', province='江苏省', city='扬州市', district='宝应县'}
Student{id=50, classId=4, stuName='周璟', stuGender='男', province='江苏省', city='扬州市', district='宝应县'}
成功取出池连接对象
第1页*******************************
Student{id=1, classId=1, stuName='陈涛', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
Student{id=2, classId=1, stuName='陈玉莹', stuGender='女', province='江苏省', city='盐城市', district='阜宁县'}
Student{id=3, classId=1, stuName='陈真', stuGender='男', province='安徽省', city='蚌埠市', district='五河县'}
Student{id=4, classId=1, stuName='丁聪', stuGender='男', province='江苏省', city='南通市', district='海安市'}
Student{id=5, classId=1, stuName='管若涵', stuGender='男', province='山东省', city='潍坊市', district='诸城市'}
Student{id=6, classId=1, stuName='胡正双', stuGender='男', province='江苏省', city='淮安市', district='盱眙县'}
Student{id=7, classId=1, stuName='江文涛', stuGender='男', province='安徽省', city='安庆市', district='桐城市'}
Student{id=8, classId=1, stuName='雷兵', stuGender='男', province='安徽省', city='六安市', district='霍山县'}
Student{id=9, classId=1, stuName='李楚鸿', stuGender='男', province='天津市', city='市辖区', district='滨海新'}
Student{id=10, classId=1, stuName='刘用兵', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
成功取出池连接对象
成功取出池连接对象
第9页*******************************
Student{id=81, classId=6, stuName='吴涛', stuGender='男', province='安徽省', city='宣城市', district='宁国市'}
Student{id=82, classId=6, stuName='徐磊', stuGender='男', province='安徽省', city='蚌埠市', district='怀远县'}
Student{id=83, classId=6, stuName='黄雄', stuGender='男', province='江苏省', city='盐城市', district='亭湖区'}
Student{id=84, classId=6, stuName='蔡志远', stuGender='男', province='江苏省', city='南通市', district='如皋市'}
Student{id=85, classId=6, stuName='陈稼轩', stuGender='男', province='安徽省', city='马鞍山', district='市和县'}
Student{id=86, classId=6, stuName='周继伟', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
第3页*******************************
Student{id=21, classId=2, stuName='徐凯强', stuGender='男', province='江苏省', city='扬州市', district='宝应县'}
Student{id=22, classId=2, stuName='姚成耀', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
Student{id=87, classId=6, stuName='陈军华', stuGender='男', province='江苏省', city='南京市', district='江宁区'}
Student{id=88, classId=6, stuName='秦超', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
Student{id=23, classId=2, stuName='应春雨', stuGender='女', province='安徽省', city='宣城市', district='宁国市'}
Student{id=24, classId=2, stuName='余杰', stuGender='男', province='安徽省', city='蚌埠市', district='怀远县'}
Student{id=25, classId=2, stuName='张超', stuGender='男', province='江苏省', city='盐城市', district='亭湖区'}
Student{id=89, classId=6, stuName='邵娅', stuGender='女', province='江苏省', city='盐城市', district='阜宁县'}
Student{id=26, classId=2, stuName='张志强', stuGender='男', province='江苏省', city='南通市', district='如皋市'}
Student{id=27, classId=2, stuName='张梓尧', stuGender='男', province='安徽省', city='马鞍山', district='市和县'}
Student{id=28, classId=2, stuName='黄超杰', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
Student{id=29, classId=2, stuName='李刚', stuGender='男', province='江苏省', city='南京市', district='江宁区'}
Student{id=30, classId=2, stuName='吴凡', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
成功取出池连接对象
第6页*******************************
Student{id=51, classId=4, stuName='苏金峰', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
Student{id=52, classId=4, stuName='吴凯', stuGender='男', province='安徽省', city='宣城市', district='宁国市'}
Student{id=53, classId=4, stuName='王纪文', stuGender='男', province='安徽省', city='蚌埠市', district='怀远县'}
Student{id=54, classId=4, stuName='孟涛涛', stuGender='男', province='江苏省', city='盐城市', district='亭湖区'}
Student{id=55, classId=4, stuName='李志杰', stuGender='男', province='江苏省', city='南通市', district='如皋市'}
Student{id=56, classId=4, stuName='丰笛', stuGender='男', province='安徽省', city='马鞍山', district='市和县'}
Student{id=57, classId=4, stuName='周炜', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
Student{id=58, classId=4, stuName='张小港', stuGender='男', province='江苏省', city='南京市', district='江宁区'}
Student{id=59, classId=4, stuName='汪小天', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
Student{id=60, classId=4, stuName='花磊', stuGender='男', province='江苏省', city='盐城市', district='阜宁县'}
成功取出池连接对象
成功取出池连接对象
第8页*******************************
Student{id=71, classId=5, stuName='关敬元', stuGender='男', province='江西省', city='抚州市', district='乐安县'}
Student{id=72, classId=5, stuName='陈明秋', stuGender='男', province='江苏省', city='泰州市', district='泰兴市'}
Student{id=73, classId=5, stuName='李国旗', stuGender='男', province='安徽省', city='亳州市', district='涡阳县'}
Student{id=74, classId=5, stuName='韩睿刘洋', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
Student{id=75, classId=5, stuName='张全根', stuGender='男', province='上海市', city='市辖区', district='浦东新'}
Student{id=76, classId=6, stuName='王跃', stuGender='男', province='江苏省', city='泰州市', district='泰兴市'}
Student{id=77, classId=6, stuName='王伟', stuGender='男', province='江苏省', city='南京市', district='高淳区'}
Student{id=78, classId=6, stuName='谢武阳', stuGender='男', province='江苏省', city='扬州市', district='宝应县'}
Student{id=79, classId=6, stuName='袁潇凯', stuGender='男', province='江苏省', city='扬州市', district='宝应县'}
Student{id=80, classId=6, stuName='邢慧斌', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
第4页*******************************
Student{id=31, classId=3, stuName='殷狄', stuGender='男', province='江苏省', city='盐城市', district='阜宁县'}
Student{id=32, classId=3, stuName='吴衍文', stuGender='男', province='安徽省', city='蚌埠市', district='五河县'}
Student{id=33, classId=3, stuName='范新宇', stuGender='男', province='江苏省', city='南通市', district='海安市'}
Student{id=34, classId=3, stuName='张琪', stuGender='男', province='山东省', city='潍坊市', district='诸城市'}
Student{id=35, classId=3, stuName='凌宇翔', stuGender='男', province='江苏省', city='淮安市', district='盱眙县'}
Student{id=36, classId=3, stuName='贾仲羽', stuGender='男', province='安徽省', city='安庆市', district='桐城市'}
Student{id=37, classId=3, stuName='黄鑫', stuGender='男', province='安徽省', city='六安市', district='霍山县'}
Student{id=38, classId=3, stuName='胡妙妙', stuGender='女', province='天津市', city='市辖区', district='滨海新'}
Student{id=39, classId=3, stuName='张啸尘', stuGender='男', province='安徽省', city='安庆市', district='望江县'}
Student{id=40, classId=3, stuName='许梦娟', stuGender='女', province='江苏省', city='南通市', district='如东县'}
成功取出池连接对象
第2页*******************************
Student{id=11, classId=1, stuName='钱一鸣', stuGender='男', province='江苏省', city='南通市', district='如东县'}
Student{id=12, classId=1, stuName='苏郑梓梵', stuGender='男', province='云南省', city='楚雄彝', district='族自治'}
Student{id=13, classId=1, stuName='唐宇飞', stuGender='男', province='江西省', city='抚州市', district='乐安县'}
Student{id=14, classId=1, stuName='王波', stuGender='男', province='江苏省', city='泰州市', district='泰兴市'}
Student{id=15, classId=1, stuName='王春雷', stuGender='男', province='安徽省', city='亳州市', district='涡阳县'}
Student{id=16, classId=1, stuName='王亮', stuGender='男', province='江苏省', city='南京市', district='浦口区'}
Student{id=17, classId=2, stuName='王志远', stuGender='男', province='上海市', city='市辖区', district='浦东新'}
Student{id=18, classId=2, stuName='吴彦祥', stuGender='男', province='江苏省', city='泰州市', district='泰兴市'}
Student{id=19, classId=2, stuName='吴志远', stuGender='男', province='江苏省', city='南京市', district='高淳区'}
Student{id=20, classId=2, stuName='相羽', stuGender='男', province='江苏省', city='扬州市', district='宝应县'}
Process finished with exit code -1