DAO设计
什么是DAO
里面包含用来操作数据库各种方法,操作数据库时只需要调用其中的方法
编写DAO接口
首先一个表对应一个类,一个对象对应一条记录
这样就可以以面向对象的思想来操作数据库
/* 类中的三个属性就对应表中的三列字段 */
public class Student {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
操作student的dao接口,包含需要的各种操作
比如添加一条学生记录:
Student stu = new Student();
stu.setId(1);
stu.setName("张三");
stu.setAge("19");
/*
创建完一个对象 ,
然后想把它添加到表中
*/
IStudentDao dao = new StuDaoImp();
dao.add(stu);// 这样就可以完成插入操作了
DAO接口
public interface IStudentDao {
/**
* 保存一个学生
*/
public void add(Student stu);
/**
* 删除学生
*/
public void delete(int id);
/**
* 更新一个学生信息
*/
public void update(int id,Student stu);
/**
* 获取指定学生
*/
public Student get(int id);
/**
* 获取所有的学生
*/
public List<Student> getAll();
/**
* 获取学生的总数
*/
Integer getCount();
}
DAO接口的实现
public class StuDaoImp implements DAO {
@Override
public void add(Student stu) {
String sql = "insert into student values(?,?,?)";
Connection con = null;
PreparedStatement prepareStatement = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mydb?serverTimeZone=Asia/Shanghai";
String user = "root";
String psw = "123456"
con = DriverManage.getConnection(url,user,psw);
prepareStatement = con.prepareStatement(sql);
prepareStatement.setInt(1,stu.getInt);
prepareStatement.setString(2,stu.getName);
prepareStatement.setInt(3,stu.getAge);
prepareStatement.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
try{
if(prepareStatement!=null){
prepareStatement.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
/*
由于每个操作方法中都会有设置预编译语句的操作
这些逻辑是重复的,不同的只是每个方法sql不同
即可以写一个RunSQL类
当中的方法是专门处理这些的固定模板
故只需要用相同的方法传入不同的sql语句和不同的参数
之前要写20行的代码,下面就只需要两行
*/
@Override
public void update(int id,Student stu) {
String sql = "update student set name = ?,age = ? where id = ?";
RunSQL.update(sql, stu.getName(), stu.getAge(), id);
}
@Override
public void delete(int id) {
String sql = "delete from student where id = ?";
RunSQL.update(sql, id);
}
@Override
public List<Student> get(int id) {
String sql = "select * from student where id = ?";
return RunSQL.query(sql, id);
}
@Override
public List<Student> getAll() {
String sql = "select * from student";
return RunSQL.query(sql);
}
}
上面使用的工具类
模板工具类:
RunSQL用来执行语句的模板
public class RunSQL {
/* 此类只是负责执行语句逻辑的模板,
每次执行语句需要加载驱动,创建连接,还有关闭资源
也就是说下面两个方法都需要去写重复的这些无关步骤而且很长
故可以封装一个类专门管连接和关闭,下面用的Database类就是
*/
public static void update(String sql,Object...x) {
Connection con = null;
PreparedStatement prepareStatement = null;
try {
con = Database.getConnection();// 获取连接对象
/*
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mydb?serverTimeZone=Asia/Shanghai";
String user = "root";
String psw = "123456"
con = DriverManage.getConnection(url,user,psw);
*/
prepareStatement = con.prepareStatement(sql);
for(int i = 0;i < x.length; i++) {
prepareStatement.setObject(i+1,x[i]);
}
prepareStatement.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
Database.close(con, prepareStatement, null);// 关闭资源
}
}
public static List query(String sql,Object...x) {
Connection con = null;
PreparedStatement sta = null;
ResultSet re = null;
List stulist = new ArrayList();
try {
con = Database.getConnection();// 获取连接,由专门的类来执行减少大量的重复
sta = con.prepareStatement(sql);
for(int i = 0; i < x.length; i++) {
sta.setObject(i+1,x[i]);
}
re = sta.executeQuery();
while(re.next()){
Student stu = new Student();
stu.setInt(re.getInt("id"));
stu.setString(re.getString("name"));
stu.setInt(re.getInt("age"));
stulist.add(stu);
}
return stulist;
}catch (Exception e) {
e.printStackTrace();
}finally {
Database.close(con, sta, re);// 同样还有关闭
}
return null;
}
}
配置文件用来存储连接需要的信息,用连接池来读取它创建有限个连接对象
/* 创建propertise文件存储数据库连接信息(user,password,url) */
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai
username=root
password=123456
Database类用来得到连接对象,还有关闭资源。这些工作调用这个类的方法就可以。免得每次的操作都要写上创建连接,和一连串的关闭。通过连接池来创建优化了运行不用以重新新建的方式
public class Database {
public static DataSource ds = null;
static {
try {
Properties properties = new Properties();
FileInputStream in = new FileInputStream("source/db.propertise");
properties.load(in);
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
return ds.getConnection();
}catch(Exception e) {
e.printStackTrace();
}
return null;
}
public static void close(Connection con,Statement sta,ResultSet re) {
if(re != null) {
try {
re.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(sta != null) {
try {
sta.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
总结
虽然通过上述构造了一些DAO还有一些工具类,但是有一个大的问题,在RunSQL的query方法中“去遍历结果集把每条记录变成一个一个对象再存到列表当中”这个过程当中是我们知道Student类中或者表中就是这三个属性。我们新建student对象并给其属性设值,这都是写好了。如果来个课程表写个课程的domain类它里面是另外三个属性(课程,姓名 ,学号)。这样就用不了RunSQL
while(re.next()){
Student stu = new Student();
stu.setInt(re.getInt("id"));
stu.setString(re.getString("name"));
stu.setInt(re.getInt("age"));
stulist.add(stu);
}
要做到通用,首先
执行语句之后得到的结果集,并不知道有几列也不知道字段名
先创建一个Object对象(不知道是哪个domain,即哪个domain都可以用)
给这个未知对象设置属性:
怎么知道它这个结果集的列名
只有知道列字段名才能得到这个字段的值re.getObject("字段名")
才能给这个对象对应的属性设值obj.setObject(re.getObject)
在写设计domain类时,每个属性名与对列名相同
那么问题就转化为怎么获取一个domain类的属性名
这样的话就可以用内省的方式解决