JDBC-MySql 02 数据访问和DAO模式

3 篇文章 0 订阅
2 篇文章 0 订阅

数据访问和DAO模式

一、学习目标
  1. 掌握DAO模式
  2. 掌握Properties配置文件的使用方法

二、数据持久化
2.1数据持久化概念
  • 将程序中的数据在瞬时状态和持久状态间转换的机制即为 数据持久化

在这里插入图片描述

2.2持久化的实现方式
  1. 数据库
  2. 普通文件
  3. XML文件

在这里插入图片描述


三、为什么进行JDBC封装

下面代码有什么缺点?

package com.aiden.jdbc;

import java.sql.*;
import java.util.Scanner;

/**
 * @author Aiden
 */
public class UserLogin {
    public static void main(String[] args) {
        //业务操作提示
        Scanner input=new Scanner(System.in);
        System.out.println("请输入用户名:");
        String userName=input.next();//用户名

        System.out.println("请输入密码:");
        String userPwd=input.next();//密码
		//访问数据库
        boolean isLogin = login(userName, userPwd);
        //业务操作判断
        if(isLogin){
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败!用户名密码错误");
        }
 }
   	//登录验证方法
 public  static  boolean login(String userName,String userPwd){
       boolean isLogin=false;
         //访问数据库
        Connection conn =null;
        PreparedStatement stmt=null;
        ResultSet rs=null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/t147hospital?serverTimeZone=UTC","root","root");
             //1.sql语句
            String sql="select * from user where userName=? and userPwd=?";
            //2.实例化执行对象prepareStatement
            stmt=conn.prepareStatement(sql);
            //3.设置参数
            stmt.setObject(1,userName);
            stmt.setObject(2,userPwd);
            //4.调用执行方法
            rs=stmt.executeQuery();
            if (rs!=null&&rs.next()){
                isLogin=true;
            }else{
                isLogin=false;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            try {
                if(rs!=null){
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(stmt!=null){
                    stmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(conn!=null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return  isLogin;
    }
    
   }

存在问题:

在这里插入图片描述

解决方案:

采用面向接口编程,可以降低代码间的耦合性

面向接口编程优势

  1. 隔离业务逻辑代码和数据访问代码
  2. 隔离不同数据库的实现

在这里插入图片描述


四、JDBC的初步封装实现

分析:

  • 通用的数据访问操作:
    • 数据库连接的建立和关闭操作
    • 增、删、改 查方法
  • 是否能进一步优化代码 ???

解决方案:

  • 将通用的操作(打开、关闭连接等)封装到工具类 BaseDao

BaseDao封装类

  • 包的命名:小写 com.hospital.dao
package com.hospital.dao;

import java.sql.*;

/**
 * 数据库访问工具类
 *
 * @author Aiden
 */
public class BaseDao {
    private String driver = "com.mysql.cj.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/t147hospital?serverTimezone=Asia/Shanghai";
    private String user = "root";
    private String password = "root";

    protected Connection conn;//连接对象

    /**
     * 建立连接
     *
     * @return
     */
    public Connection getConnection() {
        try {
            //加载驱动
            Class.forName(driver);
            //建立连接
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 关闭资源
     *
     * @param conn 连接对象
     * @param stmt 执行对象
     * @param rs   结果集
     */
    public void closeAll(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 增 删 改 方法
     * 无参:int result=executeUpdate(sql);
     * 有参:int result=executeUpdate(sql,id);
     * 有参:int result=executeUpdate(sql,name,age,sex);
     *
     * @param sql    要执行的sql命令
     * @param params 参数(可选参数)
     * @return 受影响的行数 int类型
     * @throws SQLException
     */
    public int executeUpdate(String sql, Object... params) throws SQLException {
        int result = 0;
        //声明执行对象
        PreparedStatement stmt = null;
        try {
            //建立连接
            this.conn = getConnection();
            //实例化执行对象
            stmt = conn.prepareStatement(sql);
            //如果有参数则进行参数设置
            if (params != null && params.length > 0) {
                //循环设置参数
                for (int i = 0; i < params.length; i++) {
                    stmt.setObject((i + 1), params[i]);
                }
            }
            //开始执行
            result = stmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("访问数据库异常!", e);
        } finally {
            //关闭资源
            this.closeAll(conn, stmt, null);
        }
        return result;
    }


    /**
     * 查询方法
     * (需要手动释放资源)
     * 无参:ResultSet rs=executeQuery(sql);
     * 有参:ResultSet rs=executeQuery(sql,name);
     * 有参:ResultSet rs=executeQuery(sql,name,sex);
     *
     * @param sql    要执行的sql命令
     * @param params 参数(可选参数)
     * @return ResultSet 结果集对象
     * @throws SQLException
     */
    public ResultSet executeQuery(String sql, Object... params) throws SQLException {
        //结果集
        ResultSet rs = null;
        //声明执行对象
        PreparedStatement stmt = null;
        try {
            //建立连接
            this.conn = getConnection();
            //实例化执行对象
            stmt = conn.prepareStatement(sql);
            //如果有参数则进行参数设置
            if (params != null && params.length > 0) {
                //循环设置参数
                for (int i = 0; i < params.length; i++) {
                    stmt.setObject((i + 1), params[i]);
                }
            }
            //执行查询请求
            rs = stmt.executeQuery();
        } catch (SQLException e) {
            throw new RuntimeException("访问数据库异常!", e);
        }
        return rs;
    }
}

测试调用

  • 数据表结构
drop database if exists `t147hospital`;
#创库
create database if not exists t147hospital;

use t147hospital;

drop table if exists `user`;
#创表
create table `user`(
	`userId` int(0) not null auto_increment comment '主键编号',
	`userName` varchar(50)  not null comment '用户名',
	`userPwd` varchar(50)  not null comment '密码',
 	primary key (`userId`) using btree
) ;

#插入模拟数据
insert into `user` values (1, 'admin', '123456');
insert into `user` values (2, 't147', '123456');
  • 测试代码
package com.hospital.test;

import com.hospital.dao.BaseDao;

import java.sql.SQLException;

/**
 * @author Aiden
*/
public class TestJDBCBaseDao extends BaseDao {
 //测试BaseDao增删改方法
 public static void main(String[] args) {
     TestJDBCBaseDao dao=new TestJDBCBaseDao();
     if (dao.delete()) {
         System.out.println("操作成功!");
     }else{
         System.out.println("操作失败!!!");
     }
 }
 //测试新增
 public boolean insert() {
     int result = 0;
     String sql = "insert into `user` (userName,userPwd) values(?,?);";
     try {
         result = executeUpdate(sql, "aiden", "123456");
     } catch (SQLException e) {
         e.printStackTrace();
     }
     return result > 0;
 }
 //测试修改
 public boolean update() {
     int result = 0;
     String sql = "update user set userPwd=? where  userId=?;";
     try {
         result = executeUpdate(sql, "666666",3);
     } catch (SQLException e) {
         e.printStackTrace();
     }
     return result > 0;
 }

 //测试删除
 public boolean delete() {
     int result = 0;
     String sql = "delete from user where  userId=?;";
     try {
         result = executeUpdate(sql, 3);
     } catch (SQLException e) {
         e.printStackTrace();
     }
     return result > 0;
 }
}

五、什么是DAO?

非常流行的数据访问模式——DAO模式

  1. Data Access Object(数据存取对象)

  2. 位于业务逻辑和持久化数据之间

  3. 实现对持久化数据的访问

在这里插入图片描述

五、DAO模式的组成与介绍
5.1组成部分
  1. 数据库工具类 BaseDao =>位于 dao 包下

  2. 实体类 => 位于 entity 包下

  3. DAO接口 =>位于 dao 包下

  4. DAO实现类 =>位于 dao.impl 包下

在这里插入图片描述

5.2实体类(entity)
  • 实体类(Entity)是Java应用程序中与数据库表对应的类
  • 用于存储数据,并提供对这些数据的访问
  • 通常,实现类是持久的,需要存储于文件或数据库中
  • 访问操作数据库时,以实体类的方式组织数据库中的实体及关系
  • 通常,在Java工程中创建一个名为entity的Package,用于集中保存实体类
  • 一个数据库表对应一个实体类
  • 实体类特征
    • 属性一般使用private修饰
    • 提供public修饰的getter/setter方法
    • 实体类提供无参构造方法,根据业务提供有参构造
    • 实现java.io.Serializable接口,支持序列化机制,可以将该对象转换成字节序列而保存在磁盘上或在网络上传输
  • 如果实体类实现了java.io.Serializable接口,应该定义属性serialVersionUID,解决不同版本之间的序列化问题
  • serialVersionUID赋值的方法
  • 一旦为一个实体类的serialVersionUID赋值,就不要再修改;否则,在反序列化之前版本的数据时,会报java.io.InvalidClassException异常
5.3定义实体类
package com.hospital.entity;

import java.io.Serializable;
import java.util.Date;

/**
 * 病人信息实体类
 */
public class Patient implements Serializable {
    
    //定义属性serialVersionUID,解决不同版本之间的序列化问题
    private static final long serialVersionUID = -6418684824125291382L;

    //属性一般使用`private`修饰
    private Integer patientId;//病人编号
    private String password;//密码
    private Date birthDate;//出生日期
    private String gender;//性别
    private String patientName;//姓名
    private String phoneNum;//手机
    private String email;//邮箱
    private String identityNum;//身份证
    private String address;//联系地址

    //无参构造
    public Patient() { }
    //有参构造
    public Patient(Integer patientId, String password, Date birthDate, String gender, String patientName, String phoneNum, String email, String identityNum, String address) {
        this.patientId = patientId;
        this.password = password;
        this.birthDate = birthDate;
        this.gender = gender;
        this.patientName = patientName;
        this.phoneNum = phoneNum;
        this.email = email;
        this.identityNum = identityNum;
        this.address = address;
    }
    //getter/setter方法
    public Integer getPatientId() {
        return patientId;
    }

    public void setPatientId(Integer patientId) {
        this.patientId = patientId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPatientName() {
        return patientName;
    }

    public void setPatientName(String patientName) {
        this.patientName = patientName;
    }

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getIdentityNum() {
        return identityNum;
    }

    public void setIdentityNum(String identityNum) {
        this.identityNum = identityNum;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
5.4使用实体类传递数据
@Override
public List<Patient> findAll() throws SQLException {
    String sql = "select patientId,patientName,gender,birthDate,email from patient";
    List<Patient> list = new ArrayList<>();
    //执行查询请求,并将查询保存至结果集ResultSet中
    ResultSet rs = this.executeQuery(sql);
    //循环取出结果集中的数据,并添加至泛型集合List中,以此实现使用实体类传递数据
    while (rs.next()) {
        Patient patient = new Patient();
        patient.setPatientId(rs.getInt("patientId"));
        patient.setPatientName(rs.getString("patientName"));
        patient.setGender(rs.getString("gender"));
        patient.setBirthDate(rs.getDate("birthDate"));
        patient.setEmail(rs.getString("email"));
        list.add(patient);//循环添加数据
    }
    //关闭资源
    this.closeAll(conn, null, rs);
    //返回最终从结果集获得的集合数据
    return list;
}
5.5Dao接口
package com.hospital.dao;

import com.hospital.entity.Patient;

import java.sql.SQLException;
import java.util.List;

/**
 * 病人DAO接口
 *
 * @author Aiden
 */
public interface PatientDao {
    /**
     * 查询多条数据
     *
     * @return
     */
    List<Patient> findAll() throws SQLException;

    /**
     * 根据病人名字模糊查询
     *
     * @param patientName 病人姓名
     * @return
     */
    List<Patient> findByPatientName(String patientName) throws SQLException;

    /**
     * 查询单个对象
     *
     * @param patientId
     * @return
     */
    Patient findById(Integer patientId) throws SQLException;

    /**
     * 新增
     *
     * @param patient 病人对象
     * @return
     */
    int insert(Patient patient) throws SQLException;

    /**
     * 修改
     *
     * @param patient 病人对象
     * @return
     */
    int update(Patient patient) throws SQLException;

    /**
     * 删除
     *
     * @param patientId 病人编号
     * @return
     */
    int delete(Integer patientId) throws SQLException;

}
5.6Dao实现
package com.hospital.dao.impl;

import com.hospital.dao.BaseDao;
import com.hospital.dao.PatientDao;
import com.hospital.entity.Patient;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Dao接口实现类
 *
 * @author Aiden
 */
public class PatientDaoImpl extends BaseDao implements PatientDao {

    @Override
    public List<Patient> findAll() throws SQLException {
        String sql = "select patientId,patientName,gender,birthDate,email from patient";
        List<Patient> list = new ArrayList<>();
        //执行查询请求,并将查询保存至结果集ResultSet中
        ResultSet rs = this.executeQuery(sql);
        //循环取出结果集中的数据,并添加至泛型集合List中,以此实现使用实体类传递数据
        while (rs.next()) {
            Patient patient = new Patient();
            patient.setPatientId(rs.getInt("patientId"));
            patient.setPatientName(rs.getString("patientName"));
            patient.setGender(rs.getString("gender"));
            patient.setBirthDate(rs.getDate("birthDate"));
            patient.setEmail(rs.getString("email"));
            list.add(patient);//循环添加数据
        }
        //关闭资源
        this.closeAll(conn, null, rs);
        return list;
    }

    @Override
    public List<Patient> findByPatientName(String patientName) throws SQLException {
        String sql = "select patientId,patientName,gender,birthDate,email from patient where patientName like  CONCAT('%',?,'%')";
        List<Patient> list = new ArrayList<>();
        //执行查询请求,并将查询保存至结果集ResultSet中
        ResultSet rs = this.executeQuery(sql, patientName);
        //循环取出结果集中的数据,并添加至泛型集合List中,以此实现使用实体类传递数据
        while (rs.next()) {
            Patient patient = new Patient();
            patient.setPatientId(rs.getInt("patientId"));
            patient.setPatientName(rs.getString("patientName"));
            patient.setGender(rs.getString("gender"));
            patient.setBirthDate(rs.getDate("birthDate"));
            patient.setEmail(rs.getString("email"));
            list.add(patient);//循环添加数据
        }
        //关闭资源
        this.closeAll(conn, null, rs);
        return list;
    }

    @Override
    public Patient findById(Integer patientId) throws SQLException {
        String sql = "select patientId,patientName,gender,birthDate,email from patient where patientId =?";
        Patient patient = null;
        ResultSet rs = this.executeQuery(sql, patientId);
        //判断是否存在数据,如果有则取出存放到Patient实体类中,以此实现使用实体类传递数据
        if (rs.next()) {
            patient = new Patient();
            patient.setPatientId(rs.getInt("patientId"));
            patient.setPatientName(rs.getString("patientName"));
            patient.setGender(rs.getString("gender"));
            patient.setBirthDate(rs.getDate("birthDate"));
            patient.setEmail(rs.getString("email"));
        }
        //关闭资源
        this.closeAll(conn, null, rs);
        return patient;
    }

    @Override
    public int insert(Patient patient) throws SQLException {
        String sql = "INSERT INTO `patient`(`password`, `birthDate`, `gender`, `patientName`, `phoneNum`, `email`, `identityNum`, `address`) VALUES (?,?,?,?,?,?,?,?)";
        return this.executeUpdate(sql, patient.getPassword(), patient.getBirthDate(), patient.getGender(), patient.getPatientName(), patient.getPhoneNum(), patient.getEmail(), patient.getIdentityNum(), patient.getAddress());
    }

    @Override
    public int update(Patient patient) throws SQLException {
        return 0;
    }

    @Override
    public int delete(Integer patientId) throws SQLException {
        String sql = "delete from patient where patientId=?";
        return this.executeUpdate(sql, patientId);
    }
}

六、Properties类
6.0为什么使用Properties类

使用JDBC技术访问数据库数据的关键代码

  • 如下代码不难发现,当我们数据库密码修改后,此时我们java源代码也要重新修改编译!
  • 在真实项目开发部署中,我们往往将程序中经常变动的信息以配置文件存储,让用户脱离程序本身修改相关的变量设置,而不需要重新修改java源代码,省去编译部署环节,也让程序的可扩展性得到很好的利用。
package com.hospital.dao;
import java.sql.*;
/**
 * 数据库访问工具类
 */
public class BaseDao {
 private String driver = "com.mysql.cj.jdbc.Driver";
 private String url = "jdbc:mysql://localhost:3306/t147hospital?serverTimezone=Asia/Shanghai";
 private String user = "root";
 private String password = "root";
 
 protected Connection conn;//连接对象
 public Connection getConnection() {
     try {           
         Class.forName(driver); //加载驱动
         conn = DriverManager.getConnection(url, user, password);//建立连接
     } catch (ClassNotFoundException | SQLException e) {
         e.printStackTrace();
     }
     return conn;
 }
}
6.1使用配置文件优势
  • Java中的配置文件常为xxx.properties文件
  • 后缀为.properties
  • 格式是“键=值”格式
  • 使用“#”来注释
  • 通常,为数据库访问添加的配置文件是database.properties

在这里插入图片描述

6.2创建database.properties文件
#MySql8.0驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
#MySql8.0数据库连接地址信息(含主机localhost、端口3306、数据库t147hospital、启用Unicode字符集useUnicode=true、字符编码为characterEncoding=utf8、与服务器进行通信时不使用SSL、时区=Asia/Shanghai 等设置)
jdbc.url=jdbc:mysql://localhost:3306/t147hospital?allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
#MySql8.0数据库用户名
jdbc.username=root
#MySql8.0数据库密码
jdbc.password=root
6.2Properties类读取配置

Java中提供了Properties类来读取配置文件

方法名说 明
String getProperty(String key)用指定的键在此属性列表中搜索属性。通过参数key得到其所对应的值
Object setProperty(String key,String value)调用Hashtable的方法put。通过调用基类的put()方法来设置键-值对
void load(InputStream inStream)从输入流中读取属性列表 (键和元素对)。通过对指定文件进行装载获取该文件中所有键-值对
void clear()清除所装载的键-值对,该方法由基类Hashtable提供

创建ConfigManager类

package com.hospital.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 配置文件操作工具类
 *
 * @author Aiden
 */
public class ConfigManager {
    private static Properties properties = null;

    static {
        InputStream inputStream = null;
        inputStream = ConfigManager.class.getClassLoader().getResourceAsStream("database.properties");
        if (inputStream == null) {
            throw new RuntimeException("找不到database.properties数据库参数配置文件!");
        }
        //实例化Properties对象
        properties = new Properties();
        //从输入流中读取属性列表
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            throw new RuntimeException("数据库配置参数加载错误!", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据key获取properties文件中的value值
     * @param key
     * @return
     */
    public static String getProperty(String key) {
        return properties.getProperty(key);
    }
}

改造BaseDao

public class BaseDao {
	//使用ConfigManager工具类读取配置文件的信息(注意key要保持一致,否则会出现读取不到值)
    private String driver = ConfigManager.getProperty("jdbc.driver");
    private String url = ConfigManager.getProperty("jdbc.url");
    private String user = ConfigManager.getProperty("jdbc.username");
    private String password = ConfigManager.getProperty("jdbc.password");

    protected Connection conn;//连接对象
 
    public Connection getConnection() {
        try {
            //加载驱动
            Class.forName(driver);
            //建立连接
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}
6.3读取配置时注意事项

在这里插入图片描述

七、本章总结

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值