简介
在上一篇中详细介绍了关系型数据表的一些设计(很浅显),现在来开发前的准备工作
准备阶段
分为四个阶段(分散思想还真不好搞,要我自己肯定一个类写到底)……
在这里先准备个包main,存放准备工作的类
编写数据表实体类
设计好了数据表(上一篇博客中),接下来就要为每张数据表各设立一个实体类了(这很OOP),方便在程序中直接操作数据表内容(最好写在同个包下)。
首先考虑设立一个父类,作为数据表的基类,由于每个表中都有主键列id,所以将id放入该类中(共有变量),然后发展其他类
这里以为book表为例
package main;
package database;
public class Book extends ValueObject {
//名称,简介,加个,种类W,出版社W,图片url,作者,库存
private String book_name;
private String book_intro;
private String book_price;
private String type_id_fk;
private String pub_id_fk;
private String image_url;
private String author;
private String repertory_size;
//省略setter和getter方法(我省略写在博客上可不是你让你省略……)
}
可以看到,
- 名称与数据表中字段名称一样,方便操作
- 不论数据表中的类型是什么,实体类中都是String,方便数据读取,需要该类特质的时候,再利用javaAPI转换为相应的类型。其他的表也是如此 。
读取配置文件
先写好jdbc配置文件(我的配置文件名jdbc.properties),然后写个工具类解决问题,代码如下
package main;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PropsUtil {
private static Properties props = new Properties();
private static String fileName = "jdbc.properties";
private static InputStream is;
private static String j_driver;
private static String j_url;
private static String j_user;
private static String j_pass;
static {
try {
is = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
props.load(is);
} catch (IOException e) {
e.printStackTrace();
}
j_driver = props.getProperty("jdbc.driver");
j_url = props.getProperty("jdbc.url");
j_user = props.getProperty("jdbc.user");
j_pass = props.getProperty("jdbc.pass");
}
}
很简单,就是加载文件并读取属性,不再过多解释
jdbc操作类
主要作用是执行sql语句,代码如下
package main;
import java.sql.*;
//执行jdbc操作
public class JDBCExector{
private static String driver = PropsUtil.j_driver;
private static String url = PropsUtil.j_url;
private static String user = PropsUtil.j_user;
private static String pass = PropsUtil.j_pass;
private Connection conn;
//单例类维护conn(创建conn成本巨大)
private static JDBCExector jdbcExector;
private Statement stmt;
private JDBCExector() throws Exception {
Class.forName(driver);
conn = DriverManager.getConnection(url,user,pass);
stmt = conn.createStatement();
}
public static JDBCExector getInstance() throws Exception {
if (jdbcExector == null){
return new JDBCExector();
}
return jdbcExector;
}
public ResultSet executeQuery(String sql) throws SQLException {
ResultSet result = stmt.executeQuery(sql);
return result;
}
public int executeUpdate(String sql) throws SQLException {
int result = -1;
//必须指定该属性才能得到主键
stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);
//得到主键
ResultSet primarys = stmt.getGeneratedKeys();
while(primarys.next()){
result = primarys.getInt(1);
}
return result;
}
}
- 定义连接属性,由PropsUtil(刚写好的配置文件工具类)中的静态变量为这些变量赋值
- 由于Connection创建成本巨大,所以使用单例类来维护,构造器私有(无法在构造器中初始化属性),只能通过自身newInstance方法获得对象,Connection和Statement在构造器中就初始化
- 接下来就是执行sql语句,查询返回结果集,或者操作返回被影响的条数(插入操作返回主键,方便对插入的对象进行操作),尤其记得返回主键必须为executeUpdate指定Statement.RETURN_GENERATED_KEYS属性
数据转换工具类
目的是将结果集包装为Collection集合(方便对元素进行操作……),代码如下
package main;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* 数据转换类
* 为数据表对应的数据赋值
*/
public class DataUtil {
//主操作
public Collection getData(Collection result, ResultSet rs,Class clazz)
throws Exception {
while(rs.next()){
//新建数据表实体类对象
Object ov = clazz.getDeclaredConstructor().newInstance();
//得到所有成员变量
Field[] fields = clazz.getDeclaredFields();
Field[] superFields = clazz.getSuperclass().getDeclaredFields();
Field[] allFields = addFields(fields,superFields);
for (Field f : allFields){
Method setterMethod = clazz.getMethod(getSetterMethodName(f.getName()),
f.getType());
invokeMethod(rs,f,ov,setterMethod);
}
result.add(ov);
}
return result;
}
//从ResultSet中获取一个字段的数据,并调用ov的setter方法设置变量值
//参数(结果集/数据,变量/变量名称,对象/主调,方法/setter)
private static void invokeMethod(ResultSet rs,Field field,
Object ov,Method setterMethod) throws Exception {
//数据
String value = rs.getString(field.getName());
setterMethod.invoke(ov,value);
}
//得到成员变量的Setter方法名
private static String getSetterMethodName(String fieldName){
//首字母大写
String begin = fieldName.substring(0,1).toUpperCase();
String end = fieldName.substring(1);
String result = "set" + begin + end;
return result;
}
//数组相加的方法
private static Field[] addFields(Field[] f1,Field[] f2){
List<Field> l = new LinkedList<>();
Collections.addAll(l, f1);
Collections.addAll(l, f2);
return l.toArray(new Field[f1.length + f2.length]);
}
}
这里利用的是反射的方法,获取实体类的对象、成员变量和方法,并根据数据表赋值
- 既然是包装,传入参数肯定是集合,结果集,因为要得到结果集对应的实体类信息,所以还需要传入该实体类的类别
- getData方法:逐行读取结果集,新建实体类对象,得到所有成员变量(包括父类,这里写了一个数组相加的方法addFields),然后迭代成员变量,得到每个成员变量setter方法(getSetterMethodName方法),最后得到该字段的内容,执行ov对象的setter方法赋值
- 设计方法时要充分考虑方法可能用到的信息,并设计相应的参数列表
小结
反射部分掌握不太熟练(clazz类和实例有点混乱,而且参数列表太乱了……),设计也不太模块化,写这么浅显易懂的东西我又使用了一个多小时(编程5分钟,扯淡两小时),得加快一下学习进度了