【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
一.前言(废话)
JDBC是java数据库连接的简称,也算是一种协议,一种规定.
JDBC的规范由java定义,常用的接口和类在jdk的java.sql包下.
各个厂商均实现了java.sql下定义的接口.因此在java中访问各种数据库如oracle,mysql,sqlserver,psql等都有了统一的规范:
加载驱动......
获取连接......
使用statement进行操作.....
结果集遍历......
关闭连接......
java.sql:
二.实现一个JDBCUtil,用以获取连接和关闭连接.
特点:从外部加载配置文件,使用static{ }对属性进行初始化,然后使用单例模式实现线程安全(单线程非必要)
package com.rk.test1;
//省略import各种包
/*
* JDBCUtil 实现了获取数据库连接,关闭数据库连接等操作.
*
*
*
* 这个类的特点:
* 1.从外部配置文件读取数据库信息,与具体的数据库驱动解耦,可直接使用oracle的驱动,也可以直接使用mysql,sqlserver的驱动
* 2.静态单例模式,该类只能提供一个全局的(static) Connection实例,防止过多的connection占用大量系统资源.
* */
public class JDBCUtil {
private static String driver;
private static String url;
private static String user;
private static String password;
private static Connection conn = null;
//从配置文件初始化数据库连接信息
static {
InputStream inStream =
JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
//初始化driver url name password
Properties p = new Properties();
try {
p.load(inStream);
//初始化参数
driver = p.getProperty("driver");
url = p.getProperty("url");
user = p.getProperty("user");
password = p.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
//1.加载驱动 利用Class.forName()类加载器进行加载driver,
try {
Class.forName(driver);
//双重校验锁保证获取connection是线程安全的(单线程程序中非必要)
if(conn == null) {
synchronized(JDBCUtil.class){
if(conn == null)
conn = DriverManager.getConnection(url, user, password);
}
}
return conn;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//关闭方法
public static void close() {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
conn = null;
}
}
这是一个通用的JDBCUtil,用以提供数据库连接和关闭数据库连接.
三.对JDBCUtil进行扩展,使用通用的增删改方法
在实际使用过程中,普遍使用PreparedStatement进行参数化查询.而这过程中增删改这三个方法执行的过程如出一辙,我们可以使用这样的一个通用方法来定义增删改:
public static int addDeleteUpdate(String sql,Object ...args) {
//2.方法执行必须在数据库连接之后操作,若没有连接数据库则抛出异常
if(conn == null) {
try {
throw new Exception("没有获取数据库连接!");
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
PreparedStatement pstmt = null;
try {
//准备pstmt
pstmt = conn.prepareStatement(sql);
//sql参数设置
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i+1, args[i]);
}
//执行
int rs = pstmt.executeUpdate();
//返回
return rs;
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return 0;
}
这个方法可以定义在JDBCUtil这个类中,然后在这个类中在创建一个静态内部类用于提供这些方法.当然我们还是先看一下这三个方法:
//通用删除方法 add delete update select
public static int insert(String sql,Object ...args) {
return addDeleteUpdate(sql,args);
}
public static int delete(String sql, Object ...args) {
return addDeleteUpdate(sql,args);
}
public static int update(String sql, Object ...args) {
return addDeleteUpdate(sql,args);
}
有没有感受到,瞬间增删改变得简单明了!
那么select方法为什么要和增删改区分开来呢?
因为select方法返回的是一个结果集,ResultSet,模型与增删改不一样,因此我们需要重新写一个通用的select方法.
四,通用的查找方法(查找一个和多个模型类似)
如何定义一个通用的查找方法呢?
首先我们明白,查找在得到ResultSet前的代码应该是一致的:
//获取连接,使用pstmt执行excuteQuery()
不同点在于获取结果集之后如何处理.如查找t_admin数据库表和t_user会得出不一样的列名和值.
因此在处理结果集的时候需要使用不同的类型--泛型来处理:
我们这样定义这个方法:
public static <T> T select(Class<T> clazz,String sql, Object ...args) {
//.....
}
public static 是可选的,视情况而定嘛.
<T>表示该方法为泛型方法,方法持有泛型T,后面的返回值类型为T,传入T类型的类,用于制造T类型的实例.
前面一部分代码用于得到ResultSet,应该为:
if(conn == null) {
try {
throw new Exception("没有获取数据库连接!");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
PreparedStatement pstmt = null;
try {
//准备pstmt
pstmt = conn.prepareStatement(sql);
//sql参数设置
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i+1, args[i]);
}
//执行
ResultSet rs = pstmt.executeQuery();
这段代码并不完整,但其功能非常明显,获取连接,然后,查找得出结果集.
得出结果集之后就是此方法的精髓部分了.
得到结果集后,我们从结果集拿出从mysql中查找到的数据 记录,然后放在<String,Object>的一个Map中,然后接着利用反射的方法创建一个传入的类类型.
紧接着在利用反射的方法为创建的这个类调用其setter为变量进行赋值.这里的要求是entity的变量要和数据库列一 一对应.
代码如下:
//对结果集进行获取元数据操作
ResultSetMetaData rsmd= rs.getMetaData();
Map<String,Object> data = new HashMap<String,Object>();
if(rs.next()) {
for (int i = 0; i < rsmd.getColumnCount(); i++) {
data.put(rsmd.getColumnLabel(i+1), rs.getObject(i+1));
}
}
//然后反射生成对象
if(!data.isEmpty()) {
T obj = clazz.newInstance();
//利用反射进行对象赋值.
for(Entry<String,Object> entry:data.entrySet()) {
ReflectionForSet.setFileds(obj,entry.getKey(),entry.getValue());
}
return obj;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}finally {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
其中,ReflectionForSet.setFileds(obj,entry.getKey(),entry.getValue()) 为自定义方法:
package com.rk.test1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionForSet {
public static void setFileds(Object obj,String filedName,Object value) {
Method[] method = obj.getClass().getDeclaredMethods();
//获取方法
for (Method md : method) {
if(md.getName().contains("set" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1))) {
try {
//System.out.println("即将被反射调用的方法:" + md.getName());
md.invoke(obj, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
该自定义方法实现了通过查找传入的entity对象的setter方法为某个变量进行赋值操作.
五.总结
1.将数据库配置文件从外部传入,解耦代码与具体相关数据库(也可以通过配置多个驱动加载不同数据库,从而避免配置文件).
2.封装Connection的获取和close方法,提高安全和性能.
3.封装CRUD操作的基本代码,最高程度降低相同代码的书写,使得在复杂业务逻辑中代码重复率大大降低.
4.总归只是练手用,实际使用过程中也没人会这么用吧.大家都用数据连接池和DBUtils组件.不过作为学习和练手,这让我学到了好多知识.
六.最后奉献上完整的JDBCUtil代码:
JDBCUtil / ReflectionForSet / Test
-------------------------------------------------------------
JDBCUtil
package com.rk.test1;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
/*
* JDBCUtil 实现了获取数据库连接,关闭数据库连接等操作.
*
* 同时提供通用的 增删改 方法(通过匿名内部类 + 类方法实现)
*
* 这个类的特点:
* 1.从外部配置文件读取数据库信息,与具体的数据库驱动解耦,可直接使用oracle的驱动,也可以直接使用mysql,sqlserver的驱动
* 2.静态单例模式,该类只能提供一个全局的(static) Connection实例,防止过多的connection占用大量系统资源.
* 3.提供通用的增删改查方法,通过静态内部类提供,体现了工具的特点.
* 同时用户也可以不适用工具类提供的通用增删改查方法,自己定义方法也可,此时此类只提供获取connection和关闭connection方法
* */
public class JDBCUtil {
private static String driver;
private static String url;
private static String user;
private static String password;
private static Connection conn = null;
//从配置文件初始化数据库连接信息
static {
InputStream inStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
//初始化driver url name password
Properties p = new Properties();
try {
p.load(inStream);
//初始化参数
driver = p.getProperty("driver");
url = p.getProperty("url");
user = p.getProperty("user");
password = p.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
//1.加载驱动 利用Class.forName()类加载器进行加载driver,
try {
Class.forName(driver);
//双重校验锁保证获取connection是线程安全的(单线程程序中非必要)
if(conn == null) {
synchronized(JDBCUtil.class){
if(conn == null)
conn = DriverManager.getConnection(url, user, password);
}
}
return conn;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//关闭方法
public static void close() {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
conn = null;
}
/*
* 静态内部类,工具类,提供通用的增删改查方法.
*
*
* */
static class CRUDUtil{
//1.定义一个通用插入方法
public static int addDeleteUpdate(String sql,Object ...args) {
//2.方法执行必须在数据库连接之后操作,若没有连接数据库则抛出异常
if(conn == null) {
try {
throw new Exception("没有获取数据库连接!");
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
PreparedStatement pstmt = null;
try {
//准备pstmt
pstmt = conn.prepareStatement(sql);
//sql参数设置
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i+1, args[i]);
}
//执行
int rs = pstmt.executeUpdate();
//返回
return rs;
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return 0;
}
//通用删除方法 add delete update select
public static int insert(String sql,Object ...args) {
return addDeleteUpdate(sql,args);
}
public static int delete(String sql, Object ...args) {
return addDeleteUpdate(sql,args);
}
public static int update(String sql, Object ...args) {
return addDeleteUpdate(sql,args);
}
//通用的select方法
/**
* 查询某一个
* 返回值类型:泛型T
* 参数:传入一个T类型的Class,利用此Class通过反射创建一个实例对象,然后对对象进行赋值(利用反射的方法),最后对象类型
*
* */
/**
* 首先第一步得到 ResultSet
* 然后通过ResultSet获得ResultSetMetaData
* 然后获取列的别名,把列的别名和列的值(object类型,因为不知道是int还是String)放入map
* 然后遍历map,利用反射把map中的kv装载到T类型的对象中.在返回T就可以了.
*
* */
public static <T> T select(Class<T> clazz,String sql, Object ...args) {
if(conn == null) {
try {
throw new Exception("没有获取数据库连接!");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
PreparedStatement pstmt = null;
try {
//准备pstmt
pstmt = conn.prepareStatement(sql);
//sql参数设置
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i+1, args[i]);
}
//执行
ResultSet rs = pstmt.executeQuery();
//对结果集进行获取元数据操作
ResultSetMetaData rsmd= rs.getMetaData();
Map<String,Object> data = new HashMap<String,Object>();
if(rs.next()) {
for (int i = 0; i < rsmd.getColumnCount(); i++) {
data.put(rsmd.getColumnLabel(i+1), rs.getObject(i+1));
}
}
//然后反射生成对象
if(!data.isEmpty()) {
T obj = clazz.newInstance();
//利用反射进行对象赋值.
for(Entry<String,Object> entry:data.entrySet()) {
ReflectionForSet.setFileds(obj,entry.getKey(),entry.getValue());
}
return obj;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
}
}
ReflectionForSet
package com.rk.test1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionForSet {
public static void setFileds(Object obj,String filedName,Object value) {
Method[] method = obj.getClass().getDeclaredMethods();
//获取方法
for (Method md : method) {
if(md.getName().contains("set" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1))) {
try {
//System.out.println("即将被反射调用的方法:" + md.getName());
md.invoke(obj, value);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
test
package com.rk.test1;
import java.sql.Connection;
public class Test {
public static void main(String[] args) {
Connection conn = JDBCUtil.getConnection();
System.out.println(conn);
// String sql = "select * from t_user where ";
// try {
// Statement stmt = conn.createStatement();
// ResultSet rs = stmt.executeQuery(sql);
//
// while(rs.next()) {
// System.out.println("id:" + rs.getInt(1) + " stuid:" + rs.getInt(2) + " name:" + rs.getString(3) + " password:" + rs.getString(4) + " oldpasword:" + rs.getString(5));
// }
// } catch (SQLException e) {
// e.printStackTrace();
// }
//增加
// String sql = "insert into t_user(stuid,account,password0,oldpassword)values(?,?,?,?)";
// int stuid = 20140162;
// String account = "kun";
// String password0 = "kun";
// String oldpassword = "dsd";
// long startTime = System.currentTimeMillis();
// for(int i = 0; i < 2; i++) {
// JDBCUtil.CRUDUtil.insert(sql, 30140564+i+1+100001,account,password0,oldpassword);
// }
// long endTime = System.currentTimeMillis();
// System.out.println("插入完成,耗时:" + (endTime - startTime) + "ms");
//删除
// startTime = System.currentTimeMillis();
// String sql2 = "delete from t_user where password0=?";
// String password = "ruan";
// JDBCUtil.CRUDUtil.delete(sql2, password);
// endTime = System.currentTimeMillis();
// System.out.println("删除完成,耗时:" + (endTime - startTime) + "ms");
//更新
// long startTime = System.currentTimeMillis();
// String sql3 = "update t_user set account = ? where id = ?";
// String accounts = "mrruan";
// int id = 1;
// JDBCUtil.CRUDUtil.update(sql3, accounts, id);
// long endTime = System.currentTimeMillis();
// System.out.println("更新完成,耗时:" + (endTime - startTime)/1000d + "s");
//
//查找
String sql4 = "select id,userid,state from t_admin where id = ?";
Admin admin = JDBCUtil.CRUDUtil.select(Admin.class, sql4, 1);
System.out.println(admin);
JDBCUtil.close();
}
}
我才是猫爸爸: 黑群7.2,无效,依然是MD5 mismatch
CSDN-Ada助手: 恭喜你写了这么一篇有用的博客!在vue+springboot前后端分离项目中配置https确实是一个非常重要的步骤,你的经验分享让我受益匪浅。希望你能继续分享更多关于前后端分离项目配置的经验,比如如何优化前端页面加载速度或者如何提高后端接口的安全性等内容。期待你的下一篇博客!
rx159: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb9 in position 3: invalid start byte 我的一到训练模型这里就出现这种是什么情况
Mo jio: 博主 请问打标签文件夹中没有.emd文件 为什么呢?
噜噜噜啦啦啦: 请问是一定要先配置环境是吗