版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
Java JDBC操作数据库(实践项目–框架基础)
今天来做一个简单的JDBC的框架主要实现Java操作数据库的增删改查
先来看一下目录结构:
下面利用process画图工具对这个项目的结构简单的勾勒
**1 先来看一下配置文件—》**是写在一个叫db.properties中的文件里
2 链接数据库代码
这个代码主要是来连接数据库,因为有配置文件,所以以后在其他地方运行,只需要来改配置文件即可
package com.haoyu.jdbc;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.Properties;
public final class JdbcUtilPlus {
private static String driver="com.mysql.jdbc.Driver";
private static String url;
private static String user;
private static String password;
//准备一个连接池 pool池子
private static LinkedList<Connection> pool=new LinkedList<Connection>();
static {
try {
//初始化配置信息url user password prop是一种以.properties结尾的特殊文件
//里面存入数据的方式是key-value形式
Properties prop=new Properties();
//需要把db.properties这个文件加载到prop类中去读
prop.load(JdbcUtilPlus.class.getClassLoader().getResourceAsStream("db.properties"));
//依次取出值赋值给成员变量
url=prop.getProperty("url");
user=prop.getProperty("user");
password=prop.getProperty("password");
// 初始化静态变量
Class.forName(driver);
//初始化连接池中的connections
Integer counts=Integer.parseInt(prop.getProperty("counts"));
for(int i=0;i<counts;i++) {
//产生一个原生态的连接
Connection conn=DriverManager.getConnection(url, user, password);
//产生一个代理了close方法的代理连接
conn=getProxyConnection(conn);
//将代理连接放入连接池
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取java与mysql之间的sql连接
public static Connection getConnection() throws SQLException {
// Connection conn = DriverManager.getConnection(url, user, password);
//删除并拿到conneciton之前,需要看池子中是否还有连接
Connection conn=null;
if(pool!=null&&pool.size()>0) {
conn=pool.removeLast();
}
return conn;
}
// 释放资源
public static void release(Connection conn,Statement statement,ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
rs=null;
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
statement=null;
}
//这里可以这样设计,但是,oop告诉我们,最好是对象本身执行close的时候加入pool
//可是,如果要复写close方法,必须是在conn这个对象创建的时候就复写close方法
//public void close(){pool.add(conn);}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
conn=null;
}
}
//代理模式--AOP 面向切面
//动态代理Proxy--一个对象new的过程中,jvm对其进行复写编译
//已经有方法结构的类,在运行时候,对其复写操作,产生一个子类,代理类
public static Connection getProxyConnection(Connection conn) {
//Proxy java自带的一个动态代理的操作类 newProxyInstance 创建代理实例
//使用一个孕育class的摇篮,孕育一个有原本结构的class,在handler中对指定方法进行复写,返回一个复写了方法的子类
return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader()//
,new Class[] {Connection.class}//
,new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
if(method.getName().equals("close")) {
pool.add(conn);
}else {
result=method.invoke(conn, args);
}
return result;
}
});
}
}
3 操作数据库方法的接口
一些对数据库的增删改查
package com.haoyu.dao;
import java.util.List;
import com.haoyu.inter.ResultSetHandler;
//基本的dao的操作规范定义
public interface BaseDao<T> {
public void add(String sql,Object[] params);
public void update(String sql,Object[] params);
public void delete(String sql,Object[] params);
public T selectOneById(Integer id);
// public List<T> selectByConditions(String sql,Object[] params);
public T query(String sql, Object[] params,ResultSetHandler rsHandler);
public List<T> selectAll();
}
3 实现操作数据库方法
package com.haoyu.dao;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import com.haoyu.inter.BeanForListHandler;
import com.haoyu.inter.BeanHandler;
import com.haoyu.inter.ResultSetHandler;
import com.haoyu.jdbc.JdbcUtilPlus;
//泛型参数变量 子类的泛型参数决定父类的泛型参数
public class BaseDaoImpl<T> implements BaseDao<T> {
private Class clazz;
public BaseDaoImpl() {
clazz=(Class)((ParameterizedType)this.getClass()//
.getGenericSuperclass())//
.getActualTypeArguments()[0];
}
public BaseDaoImpl(int i) {
}
//sql=insert into user (id,name) values (?,?) +(ps) params= {1,"apple"}
//insert into user (id,name) values (1,'apple')
//再通过connection传递给mysql操作
private void modify(String sql,Object[] params) {
//拿到连接
Connection conn=null;
//Statement-->PreparedStatement
//-- params 拼装组合并且使用占位符?的方式,简便拼装过程
PreparedStatement ps=null;
try {
//实例化上面两个空对象
conn=JdbcUtilPlus.getConnection();
//这个快递盒必须符合运输线路定义的快递盒规范
//prepare 准备 statemenet 快递盒
ps=conn.prepareStatement(sql);
//接下来要针对说明书放组件 params 也就是给sql拼接参数
//按照说明书拼接组件 sql+params
//如果参数是空,就不需要凭借,直接执行sql,例如:insert into user (id,name) values (1,'apple')
if(params!=null&¶ms.length>0) {
//按照说明书拼装组件
for(int i=0;i<params.length;i++) {
ps.setObject(i+1,params[i]);
}
}
//放入快递盒通过连接桥梁传递过去
ps.executeUpdate();//快递盒自己调用更新执行的方法,将拼接好的sql执行到数据中
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
JdbcUtilPlus.release(conn, ps, null);
}
}
//增加数据操作
@Override
public void add(String sql, Object[] params) {
modify(sql,params);
}
//修改数据操作
@Override
public void update(String sql, Object[] params) {
modify(sql,params);
}
//删除数据操作
@Override
public void delete(String sql, Object[] params) {
modify(sql,params);
}
//查询一个数据操作
@Override
public T selectOneById(Integer id) {
String sql="select * from "+clazz.getSimpleName()+" where id=?";
Object[] params= {id};
return query(sql,params,new BeanHandler(clazz));
}
//一个查询的基本模板,可以替代上面的查询
@Override
public T query(String sql, Object[] params,ResultSetHandler rsHandler) {
//建立mysql与db的桥梁
Connection conn=null;
//物流系统,快递盒--说明书+组件
PreparedStatement ps=null;
//db返回过来的特殊数据集合结构
ResultSet rs=null;
try {
conn=JdbcUtilPlus.getConnection();//建立连接
//prepareStatement会先初始化SQL,先把这个SQL提交到数据库中进行预处理,多次使用可提高效率。
ps=conn.prepareStatement(sql);//创建容器
if(params!=null&¶ms.length>0) {
//按照说明书拼装组件
for(int i=0;i<params.length;i++) {
ps.setObject(i+1,params[i]);
}
}
//执行查询返回一个特殊结构的数据集合
rs=ps.executeQuery();
//表结构信息类--通过他取出该表有多少列,每一列叫什么名字
//该类需要通过结果集来获取
return (T) rsHandler.handler(rs);
}catch(Exception e) {
throw new RuntimeException(e+"查询出问题");
}
}
//多条查询
@Override
public List<T> selectAll() {
//得到类的简写名称
String sql="select * from "+clazz.getSimpleName();
return (List<T>) query(sql, null,new BeanForListHandler(clazz));
}
}
/*
@Override
public List<T> selectByConditions(String sql, Object[] params) {
//建立mysql与db的桥梁
Connection conn=null;
//物流系统,快递盒--说明书+组件
PreparedStatement ps=null;
//db返回过来的特殊数据集合结构
ResultSet rs=null;
try {
conn=JdbcUtilPlus.getConnection();
ps=conn.prepareStatement(sql);
if(params!=null&¶ms.length>0) {
//按照说明书拼装组件
for(int i=0;i<params.length;i++) {
ps.setObject(i+1,params[i]);
}
}
//执行查询返回一个特殊结构的数据集合
rs=ps.executeQuery();
//表结构信息类--通过他取出该表有多少列,每一列叫什么名字
//该类需要通过结果集来获取
ResultSetMetaData rsmd=rs.getMetaData();
int columns=rsmd.getColumnCount();
List<T> lists=new ArrayList<T>();
//结果集的指针从第一条数据之上指向下一条,并返回true false 代表有没有值
while(rs.next()) {//只要在循环,就意味着下一条有值
T target=(T) clazz.newInstance();
//取出的每一条数据,都要根据列名,取出每一条中每一列的值
//采用的方式就用列名取每一条数据中的对应列(一个单元格) --》龙暗1
2 龙暗1
3 龙暗2
//循环每一列
for(int i=0;i<columns;i++) {
String columnName=rsmd.getColumnLabel(i+1);
Object value=rs.getObject(columnName);
//获取stu的所有字段
Field[] fs=target.getClass().getDeclaredFields();
for(Field field:fs) {
//可访问私有成员
field.setAccessible(true);
String name=field.getName();
if(name.equals(columnName)) {
field.set(target, value);
break;
}
}
}
lists.add(target);
}
return lists;
}catch(Exception e) {
throw new RuntimeException(e+"查询出问题");
}
}*/
4 建立一个与数据表结构一样的类
package com.haoyu.bean;
public class Stu {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Stu [id=" + id + ", name=" + name + "]";
}
}
package com.haoyu.dao;
import com.haoyu.bean.Stu;
public class StuDaoImpl extends BaseDaoImpl<Stu> implements StuDao{
}
package com.haoyu.dao;
import com.haoyu.bean.Stu;
public interface StuDao extends BaseDao<Stu>{
}
5 针对多条查询和单条数据查询。
package com.haoyu.inter;
import java.sql.ResultSet;
public interface ResultSetHandler {
//结果集(ResultSet)是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等。
public Object handler(ResultSet rs);//简单的来说,就是一个返回结果集的东西
}
多条查询
package com.haoyu.inter;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
//处理多个值的handler
//返回一个值的处理方案
public class BeanForListHandler implements ResultSetHandler {
private Class clazz;
public BeanForListHandler(Class clazz) {
this.clazz = clazz;
}
@Override
public Object handler(ResultSet rs) {
try {
// 该类需要通过结果集来获取
ResultSetMetaData rsmd = rs.getMetaData();
int columns = rsmd.getColumnCount();
List list=new ArrayList();
// 结果集的指针从第一条数据之上指向下一条,并返回true false 代表有没有值
while (rs.next()) {// 只要在循环,就意味着下一条有值
Object target = clazz.newInstance();
for (int i = 0; i < columns; i++) {
String columnName = rsmd.getColumnLabel(i + 1);
Object value = rs.getObject(columnName);
// 获取stu的所有字段
Field[] fs = target.getClass().getDeclaredFields();
for (Field field : fs) {
// 可访问私有成员
field.setAccessible(true);
String name = field.getName();
if (name.equals(columnName)) {
field.set(target, value);
break;
}
}
}
list.add(target);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e + "處理結果集出問題");
}
}
}
单条查询
package com.haoyu.inter;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
//返回一个值的处理方案
public class BeanHandler implements ResultSetHandler {
private Class clazz;
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
@Override
public Object handler(ResultSet rs) {
try {
// 该类需要通过结果集来获取
//ResultSetMetaData类完成了查询结果信息和结果中的列的各种信息
//参考连接:https://blog.csdn.net/en_joker/article/details/77844529
ResultSetMetaData rsmd = rs.getMetaData();
int columns = rsmd.getColumnCount();
// 结果集的指针从第一条数据之上指向下一条,并返回true false 代表有没有值
Object target = clazz.newInstance();
while (rs.next()) {// 只要在循环,就意味着下一条有值
for (int i = 0; i < columns; i++) {
//getColumnLabel:默认的列的标题
String columnName = rsmd.getColumnLabel(i + 1);
Object value = rs.getObject(columnName);
// 获取stu的所有字段-->也就是给类的属性
Field[] fs = target.getClass().getDeclaredFields();
for (Field field : fs) {
// 可访问私有成员
field.setAccessible(true);
String name = field.getName();
if (name.equals(columnName)) {
field.set(target, value);
break;
}
}
}
}
return target;
} catch (Exception e) {
throw new RuntimeException(e + "处理结果集出问题");
}
}
}
测试类
package com.haoyu.test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import org.junit.Test;
import com.haoyu.bean.Stu;
import com.haoyu.dao.BaseDaoImpl;
import com.haoyu.dao.StuDaoImpl;
import com.haoyu.inter.BeanForListHandler;
import com.haoyu.inter.BeanHandler;
import com.haoyu.jdbc.JdbcUtilPlus;
public class JdbcTest {
@Test
public void testQueryOtherMethods() {
Stu stu=new StuDaoImpl().selectOneById(1);
System.out.println(stu);
System.out.println("-------------------");
List<Stu> stus=(List<Stu>) new StuDaoImpl().selectAll();
for(Stu stu2:stus) {
System.out.println(stu2);
}
}
@Test
public void testQueryPlus2() {
String sql="select * from stu where id>? and id<?";
Object[] params= {2,4};
Stu stu=new StuDaoImpl().query(sql, params, new BeanHandler(Stu.class));
System.out.println(stu);
}
@Test
public void testQueryPlus() {
String sql="select * from stu";
List<Stu> stus=(List<Stu>) new StuDaoImpl().query(sql, null, new BeanForListHandler(Stu.class));
for(Stu stu:stus) {
System.out.println(stu);
}
}
@Test
public void testQuery() {
// String sql="select * from stu where id>? and id<?";
// Object[] params= {2,4};
// List<Stu> stus=new StuDaoImpl().selectByConditions(sql, params);
// for(Stu stu:stus) {
// System.out.println(stu);
// }
}
@Test
public void testDelete() {
String sql="delete from stu where name=? and id=?";
Object[] params= {"龙傲天",1};
BaseDaoImpl dao=new BaseDaoImpl(1);
dao.delete(sql, params);
}
@Test
public void testDaoUpdate() {
String sql="update stu set name=? where id=?";
Object[] params= {"龙傲天",1};
BaseDaoImpl dao=new BaseDaoImpl(1);
dao.update(sql, params);
}
@Test
public void testDaoAdd() {
String sql="insert into stu (name) values (?)";
Object[] params= {"龙暗"};
BaseDaoImpl dao=new BaseDaoImpl(1);
dao.add(sql, params);
}
//将鼠标点中方法名称,然后右键执行,出现绿条即为正确,红色报错
@Test
public void testConnectionInit()throws SQLException{
//com.mysql.jdbc.JDBC4Connection@2f410acf
Connection conn=JdbcUtilPlus.getConnection();
System.out.println(conn);
JdbcUtilPlus.release(conn,null,null);
}
}