JDBC——DAO设计

DAO设计

  1. 什么是DAO
  2. 编写DAO接口
  3. DAO接口实现
  4. 编写其他工具类

什么是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类的属性名

这样的话就可以用内省的方式解决

本文分享自微信公众号 - IT那个小笔记(qq1839646816)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券