import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
/**
* @author yutang
* @version 1.0
* @ date 2023/12/5
* @ description jdbc的简单封装
*/
public class DbUtils {
private Connection conn = null;
private PreparedStatement pst = null;
private ResultSet rs = null;
// 用于读取配置文件的properties对象
private final static Properties properties = new Properties();
// 静态加载驱动
static {
try {
// 注意自己的配置文件路径
properties.load(new FileInputStream("res/jdbc.properties"));
Class.forName(properties.getProperty("driverClassName"));
} catch (Exception e) {
System.out.println("数据库驱动注册失败");
e.printStackTrace();
}
}
// 获取连接的方法
private void getConn(){
try {
conn = DriverManager.getConnection(
properties.getProperty("url"),
properties.getProperty("username"),
properties.getProperty("password"));
} catch (SQLException e) {
System.out.println("数据库连接异常");
e.printStackTrace();
}
}
// 关闭连接方法
private void close(){
if (Objects.nonNull(rs)) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if (Objects.nonNull(pst)) {
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
pst = null;
}
}
if (Objects.nonNull(conn)) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
// 将对数据库的增删改封装到一个方法
public final <V> int executeUpdate(String sql, V... params){
int result = -1;
getConn();
try {
pst = conn.prepareStatement(sql);
if (Objects.nonNull(params)){
for (int i = 0; i < params.length; i++) {
pst.setObject((i+1),params[i]);
}
}
result = pst.executeUpdate();
} catch (SQLException e) {
// 执行sql时数据库出现异常,返回-1
result = -1;
e.printStackTrace();
} finally {
close();
return result;
}
}
// 查询的方法封装,有明确的返回实体
public final <T,V> List<T> executeQuery(String sql, Class<T> clazz, V... params){
ArrayList<T> list = new ArrayList<>();
getConn();
try {
pst = conn.prepareStatement(sql);
if (Objects.nonNull(params)){
for (int i = 0; i < params.length; i++) {
pst.setObject((i+1),params[i]);
}
}
rs = pst.executeQuery();
ResultSetMetaData colsData = rs.getMetaData();
int colCount = colsData.getColumnCount();
while (rs.next()){
T instance = clazz.getDeclaredConstructor().newInstance();
for (int i = 1; i <= colCount; i++) {
Object value = rs.getObject(i);
if (Objects.nonNull(value)) {
String columnName = underlineToHump(colsData.getColumnName(i));
Field field = clazz.getDeclaredField(Objects.requireNonNull(columnName));
field.setAccessible(true);
field.set(instance,value);
}
}
list.add(instance);
}
} catch (SQLException e) {
list = null;
e.printStackTrace();
} finally {
close();
return list;
}
}
// 查询没有明确的返回实体,返回一个Map的集合
// 注意返回的map集合里面的key是驼峰命名的
public final <V> List<Map<String,Object>> executeQuery(String sql, V... params){
getConn();
try {
pst = conn.prepareStatement(sql);
if (Objects.nonNull(params)) {
for (int i = 0; i < params.length; i++) {
pst.setObject((i + 1), params[i]);
}
}
rs = pst.executeQuery();
ResultSetMetaData colsData = rs.getMetaData();
int colCount = colsData.getColumnCount();
List<Map<String,Object>> list = new ArrayList<>();
while (rs.next()){
Map<String,Object> map = new HashMap<>();
for (int i = 1; i <= colCount; i++) {
map.put(underlineToHump(colsData.getColumnName(i)),rs.getObject(i));
}
list.add(map);
}
return list;
}catch (SQLException e) {
e.printStackTrace();
return null;
} finally {
close();
}
}
// 多条sql的事务修改(如果有一条执行失败就直接回滚),注:每条sql必须执行后都有影响行数不然将会回滚
public final <V> int executeUpdateTrans(List<String> sqls,List<V[]> paramsList){
int result = -1;
getConn();
try {
int count = 0;
conn.setAutoCommit(false); // 开启事务
for (int i = 0; i < sqls.size(); i++) {
pst = conn.prepareStatement(sqls.get(i));
V[] params = paramsList.get(i);
for (int j = 0; j < params.length; j++) {
pst.setObject((j+1),params[j]);
}
count += pst.executeUpdate();
}
if (count!=sqls.size()) {
result = 0;
conn.rollback();
} else {
result = 1;
conn.commit();
}
} catch (SQLException e) {
// 执行sql时数据库出现异常,返回-1
conn.rollback(); // 回滚
result = -1;
e.printStackTrace();
} finally {
close();
return result;
}
}
/**
* 将下划线命名转驼峰,主要用途:将数据库中查询到的下划线命名的字段转为驼峰命名,便于和Java代码中的实体字段相匹配
* @param str 传入的下划线命名的字符串:user_id(数据库中的字段名称)
* @return 返回驼峰命名的参数名称字符串 userId(转换后的字段,Java实体的一般命名方式)
*/
private String underlineToHump(String str){
if (Objects.isNull(str)){
return null;
}
String[] split = str.split("_");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < split.length; i++) {
if (i==0){
sb.append(split[0].toLowerCase());
}else {
sb.append(String.valueOf(split[i].charAt(0)).toUpperCase()).append(split[i].substring(1).toLowerCase());
}
}
return sb.toString();
}
}
1.配置文件的路径一定要正确,一般实在项目目录下面创建一个res资源文件夹。
2.注意在使用事务修改方法时要保证:传入的sql语句和传入的参数集合的顺序和数量一致