JDBC操作数据库步骤
JDBC操作数据库通常分为六个通用步骤(例子为mysql数据库的连接):
- 加载驱动(JDBC4之后可以省略,需要导入相关数据库的驱动包)
- 获取连接
- 获取执行sql命令的对象
- 执行
- 处理结果
- 关闭资源
execute方法详解
Statement接口主要用于执行sql语句对象,内部包含一些执行方法:
- execute():用执行所有的sql语句,对于有结果集的sql会返回true,否则返回false
- executeUpdate():用于执行DML语句中的更新语句(insert,delete,update),返回结果为影响的数据行数
- executeQuery():用于执行查询语句(select),返回一个ResultSet(结果集)对象,内部存储的为查询的数据
Statement & PreparedStatement
Statement是由JDBC接口提供的一个用于执行静态SQL语句的接口,但是通过Statement执行SQL语句时,会存在SQL注入的风险,因此JDBC还提供了另一个接口PreparedStatement,用于解决这一问题,使用PreparedStatement对象可以对sql语句预编译,从而防止sql注入。
准备属性文件
DBUtils的封装
以mysql为例。为了不用修改java源代码,可以将连接数据库的常见字符串直接配置到properties文件中:
jdbc.properties
#mysql connection config
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mydb
user=root
password=123456
#mssqlserver connection config
#driverClass=com.miscrosoft.jdbc.sqlserver.SQLServerDriver
#url=jdbc:sqlserver://127.0.0.1:1433;databaseName=test
#user=sa
#password=123456
#oracle connection config
#driverClass=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
#user=scott
#password=tiger
封装DBUtils
import java.io.*;
import java.sql*;
import java.util.*;
/**
* 自己封装的数据库工具类,包含一些操作数据库的简便方法
* 1. 获取连接
* 2. 关闭资源
* 3. 封装通用增删改操作
* @author mrchai
*/
public class DBUtils {
public static String driverClass;
public static String url;
public static String user;
public static String password;
static{
try {
Properties prop = new Properties();
//加载属性文件
prop.load(new FileInputStream("src/jdbc.properties"));
//获取属性信息(连接数据库的相关字符串)
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
//加载驱动
Class.forName(driverClass);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return
*/
public static Connection getConn(){
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源
* @param rs
* @param stat
* @param conn
*/
public static void close(ResultSet rs,Statement stat,Connection conn){
try {
if(rs != null){
rs.close();
}
if(stat != null){
stat.close();
}
if(conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 封装通用的增、删、改操作(针对任何数据库表的更新操作(DML语句)都能通过该方法实现)
* @param sql
* @return
*/
public static int exeUpdate(String sql,Object... params){
Connection conn = null;
PreparedStatement ps = null;
try {
//获取连接
conn = getConn();
//编译sql语句获取预处理对象
ps = conn.prepareStatement(sql);
if(Objects.nonNull(params)){
for(int i = 0;i<params.length;i++){
ps.setObject(i + 1, params[i]);
}
}
//执行更新
int i = ps.executeUpdate();
return i;
} catch (SQLException e) {
e.printStackTrace();
}finally{
close(null, ps, conn);
}
return 0;
}
}
反射
反射是java中类的一种自省机制,通过反射可以在运行时获取类中的成分,并且使用,从而提高了java的动态性;java中任何一个类都有一个对应的java.lang.Class对象,这个对象包含了类中的所有成分(属性,构造器,方法,注解等),获取类的Class对象有三种方式:
- Class.forName(“类路径”) Class clz = class.forName(“com.softeem.entity.Emp”)
- 类名称.class Class clz = Emp.class
- 对象引用.getClass() Class clz = emp.getClass()
基于反射实现对象拷贝
public class ObjectCopy {
public static <T> T clone(Object source,Class<T> t){
T target = null;
try {
//获取源对象的Class对象
Class clz = source.getClass();
//创建新对象(无数据)
target = t.newInstance();
//获取源对象中的所有属性列表
Field[] fields = clz.getDeclaredFields();
for (Field f : fields) {
//获取属性名
String fname = f.getName();//ename
//获取源对象的所有get方法,并执行,将其返回值通过调用目标对象的set方法,设置给目标对象 ename setEname
//获取当前属性的setter/getter方法名
String setMethodName = "set"+fname.substring(0,1).toUpperCase()+fname.substring(1);//setEname
String getMethodName = "get"+fname.substring(0,1).toUpperCase()+fname.substring(1);//setEname
//获取方法对象
Method setMethod = clz.getMethod(setMethodName, f.getType());
Method getMethod = clz.getMethod(getMethodName);
//调用源对象的getter获取返回值 getName() getDno()
Object returnValue = getMethod.invoke(source);
//调用目标对象的setter方法设置值
setMethod.invoke(target, returnValue);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//返回产生的新对象
return target;
}
public static void main(String[] args) {
Account a = new Account(1, "测试1", new BigDecimal(3500));
Emp e = new Emp(1, "测试2", new BigDecimal("4500"), new Date(), 10);
Account obj = clone(a,Account.class);
System.out.println(obj);
Emp obj2 = clone(e,Emp.class);
System.out.println(obj2);
}
}
基于反射封装DBUtils
/**
* 封装通用的查询集合操作,对于任何形式的查询,都能返回一个对象集合
* @param t 需要返回的数据类型(泛型)
* @param sql 目标查询语句
* @param params 执行查询所需的参数
* @return 返回包含指定对象的集合
*/
public static <T> List<T> queryList(Class<T> t,String sql,Object... params){
//声明空集合
List<T> data = new ArrayList<>();
//获取查询结果信息
List<Map<String,Object>> list = getDataPair(sql, params);
if(list.isEmpty()){
return data;
}
//遍历集合
for (Map<String, Object> map : list) {
T obj = parseMapToBean(map,t);
data.add(obj);
}
return data;
}
/**
* 封装通用查询对象操作,对于任何形式的查询,都能返回一个确定的对象
* @param t 需要返回的数据类型(泛型)
* @param sql 目标查询语句
* @param params 执行查询所需的参数
* @return 返回指定对象
*/
public static <T> T queryOne(Class<T> t,String sql,Object... params){
List<Map<String,Object>> list = getDataPair(sql, params);
if(!list.isEmpty()){
Map<String,Object> map = list.get(0);
T obj = parseMapToBean(map,t);
return obj;
}
return null;
}
/**
* 将一个Map集合对象转换为一个JavaBean并返回
* @param map
* @param t
* @return
*/
private static <T> T parseMapToBean(Map<String, Object> map, Class<T> t) {
T obj = null;
try {
//创建一个空实例
obj = t.newInstance();
//获取map集合的键集(所有列名称,即要返回对象的属性名)
Set<String> keys = map.keySet();
for (String cname : keys) {
//获取属性对象
Field f = t.getDeclaredField(cname);
//获取set方法的名称
String setMethodName = "set"+cname.substring(0,1).toUpperCase()+cname.substring(1);
//获取set方法对象
Method method = t.getMethod(setMethodName, f.getType());
//执行方法
method.invoke(obj, map.get(cname));
}
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
/**
* 解析指定查询语句,并将获取的数据(列名,列值)以集合的形式返回
* @param sql
* @param params
* @return
*/
private static List<Map<String,Object>> getDataPair(String sql,Object... params){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
//声明集合存储获取的表数据(列名,列值)
List<Map<String,Object>> list = new ArrayList<>();
try {
conn = getConn();
ps = conn.prepareStatement(sql);
if(Objects.nonNull(params)){
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
rs = ps.executeQuery();
//获取结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列总数,获取列名称,获取标签名,获取列值,将相关数据存储到map集合
//获取查询的总列数
int count = rsmd.getColumnCount();
//对结果集遍历
while(rs.next()){
//对结果集每遍历一次,获取一条数据(即一个map对象)
Map<String,Object> map = new HashMap<>();
//遍历每一列
for (int i = 1; i <= count; i++) {
//获取列名称
String columnName = rsmd.getColumnName(i);
//获取标签名(列别名)
//String columnLabel = rsmd.getColumnLabel(i);
//获取列值
Object value = rs.getObject(i);
//当列值不为null时才将数据存入map集合
if(Objects.nonNull(value)){
//将数据存入map
map.put(columnName, value);
}
}
//将map集合存入List
list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
} finally{
//回收资源
DBUtils.close(rs, ps, conn);
}
return list;
}