java使用JDBC连接数据库,这里由于我只学了mysql数据库,所以只讲MySQL数据库的常用增删改查的封装。
一般的增删改写法如下:
//conn:是数据库连接
public boolean add(Object obj){
String sql="INSERT INTO table_name (column...) VALUES (?,?,?...?)";
PreparedStatement ps=conn.prepareStatement(sqll);
ps.setObject(1,obj.get...);
....
ps.setObject(n,obj.get...);
return ps.executeUpdate()>0?true:false;
}
增删改的写法格式基本都是这样,只是sql语句的不同,为了避免写大量的重复代码,我们可以把增删改进行一个封装,把sql语句抽出,写成一个方法。封装后代码大致如下
public static boolean executeUpdate(String sql, Object... params) {
// 得到用于发送SQL语句的句柄
PreparedStatement ps = conn.prepareStatement(sql);
// 设置参数
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
// 发送SQL语句
return ps.executeUpdate() > 0;
}
其中sql是你要执行的增删改的sql语句,Object…params是一个可变参数组,用来对sql语句进行赋值(拼接??),进行这么一个封装后,就避免了写大量的重复代码。
只需要把它写在一个工具类里,需要用是直接调用就行了。
查与增删改不同,它相对来说更复杂些,对查的封装涉及到了反射的应用,这会有点难以理解。首先普通的查的写法为:
//conn:是数据库连接
public List<Object> select(Object obj){
List<Object> list=new ArrayList<>();
String sql="SELECT * FROM table_name WHERE 条件1....条件n";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setObject(1,obj.get...);
....
ps.setObject(n,obj.get...);
ResultSet rs=ps.executeQuery();
while(rs.next){
Object o=new Object();
//这里的Object是你要查的对象,我只是为了说明查的普遍格式而这么写成Object的。
o.set属性名(rs.get数据类型("字段名"));
....
o.set属性名(rs.get数据类型("字段名"));
list.add(0);
}
return list;
}
查的话不管是查询全部信息还是根据某个字段或多个字段查询某一条信息,格式基本都是这样,也只是sql语句和数据封装的不同而已。
对此我们也可以进行封装,封装后代码如下;
public static <T> List<T> executeQuery(String sql, Class<T> clazz, Object... params) {
List<T> result = new ArrayList<>();
PreparedStatement ps = null;
ResultSet rs = null;
// 得到数据库句柄
ps = DatabaseUtil.getConnection().prepareStatement(sql);
// 设置参数
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
// 发送SQL语句,得到结果
rs = ps.executeQuery();
// 得到元数据
ResultSetMetaData metaData = rs.getMetaData();
// 解析封装结果
while (rs.next()) {
T data = clazz.getDeclaredConstructor().newInstance();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i);
String[] strs = columnName.split("_");
String fieldName = "";
String methodName = "set";
for (int j = 0; j < strs.length; j++) {
if (j == 0) {
fieldName += strs[j];
} else {
fieldName = fieldName + strs[j].substring(0, 1).toUpperCase() + strs[j].substring(1);
}
methodName = methodName + strs[j].substring(0, 1).toUpperCase() + strs[j].substring(1);
}
Method method = clazz.getDeclaredMethod(methodName, clazz.getDeclaredField(fieldName).getType());
method.invoke(data, rs.getObject(columnName));
}
result.add(data);
}
}
return result.size() > 0 ? result : null;
}
封装后代码就是这样的,其中sql还是sql语句,Object… params还是可变参数组,不同的是多了一个Class clazz,其中T是java中的泛型,clazz则是查询的数据的类型,可为String,Integer,也可以是自己定义的类。其返回值也为泛型T,而实际调用中clazz决定了返回值的类型。
在使用该方法时,java中的属性命名要严格遵守小驼峰命名法,数据库的字段命名要遵守单词与单词之间用下划线分割开的规则,
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i);
String[] strs = columnName.split("_");
String fieldName = "";
String methodName = "set";
for (int j = 0; j < strs.length; j++) {
if (j == 0) {
fieldName += strs[j];
} else {
fieldName = fieldName + strs[j].substring(0, 1).toUpperCase() + strs[j].substring(1);
}
methodName = methodName + strs[j].substring(0, 1).toUpperCase() + strs[j].substring(1);
}
Method method = clazz.getDeclaredMethod(methodName, clazz.getDeclaredField(fieldName).getType());
method.invoke(data, rs.getObject(columnName));
}
result.add(data);
}
否则这段对查询出来的数据进行封装的代码就会出错。
其中
**T data = clazz.getDeclaredConstructor().newInstance();**实现了clazz类的无参构造,
**Method method = clazz.getDeclaredMethod(methodName, clazz.getDeclaredField(fieldName).getType());**获得了clazz的methodName方法
method.invoke(data, rs.getObject(columnName));
调用了method方法
这三句就应用了反射。
上述代码我都没有捕捉或抛出异常,对于一些资源也没有进行释放,在实际运用中是需要对异常进行处理的,对于一些资源也是需要进行释放的。