JDBC(Java Database Conectivity)
Java数据库连接技术的简称,提供连接各种常用数据库的能力
JDBC API
内容:供程序员调用的接口与类,集成在java.sql和javax.sql包中,如
DriverManager
JDBC 驱动
JDBC API 主要功能
与数据库建立连接、执行SQL 语句、处理结果
DriverManager :依据数据库的不同,管理JDBC驱动
Connection :负责连接数据库并担任传送数据的任务
Statement :由 Connection 产生、负责执行SQL语句
esultSet:负责保存Statement执行后的查询结果
数据库版本:MySQL5.7
MySQL官网下载对应的JDBC驱动JAR包
驱动类
使用纯Java方式连接数据库
由JDBC驱动直接访问数据库
优点:完全Java代码,快速、跨平台
缺点:访问不同的数据库需要下载专用的JDBC驱动
JDBC驱动由数据库厂商提供
try {
Class.forName(JDBC驱动类);
# 1.加载JDBC驱动
} catch (ClassNotFoundException e) {
//异常输出代码
} //… …
try {
Connection con=DriverManager.getConnection(数据连接字符串,数据库用户名,密码);
// 2.与数据库建立连接
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM table1;");
// 3.发送SQL语句,并得到返回结果
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}
// 4.处理返回结果
rs.close();
stmt.close();
con.close();
// 5.释放资源
} //… …
jdbc:数据库://ip:端口/数据库名称[连接参数=参数值]
连接本地MySQL中hospital数据库
jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8
// 我国处于东八区,时区设置为GMT-8
Connection是数据库连接对象的类型
方法 | 作用 |
---|---|
Statement createStatement() | 创建一个Statement对象将SQL语句发送到数据库 |
PreparedStatement prepareStatement(String sql) | 创建一个PreparedStatement对象,将参数化的SQL语句发送到数据库 |
boolean isClosed() | 查询此Connection对象是否已经被关闭。如果已关闭,则返回true;否则返回false |
void close() | 立即释放此Connection对象的数据库和JDBC资源 |
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
public class HospitalConn {
private static Logger logger = LogManager.getLogger(HospitalConn.class.getName());
public static void main(String[] args) {
Connection conn = null;
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC","root", "root");
System.out.println("数据库连接成功");
} catch (SQLException e) {
logger.error(e);
} finally {
// 3、关闭数据库连接
try {
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
使用JDBC连接数据库时,经常出现的错误
Java执行数据库操作的一个重要接口,在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句
方法 | 作用 |
---|---|
ResultSet executeQuery(String sql) | 可以执行SQL查询并获取ResultSet对象 |
int executeUpdate(String sql) | 可以执行插入、删除、更新的操作,返回值是执行该操作所影响的行数 |
boolean execute(String sql) | 可以执行任意SQL语句。如果结果为 ResultSet 对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回false |
使用 executeQuery() 和 executeUpdate() 方法都需要啊传入 SQL 语句,因此,需要在 Java 中通过字符串拼接获得 SQL 字符串
String类
StringBuffer类
如果使用StringBuffer 生成了 String 类型字符串,可以通过 toString( ) 方法将其转换为一个 String 对象
String patientName="李明";
String gender="男";
String birthDate="2010-09-03";
//使用+拼接字符串
String sql = "insert into patient (patientName,gender,birthDate) values('"+
patientName+"','"+
gender+"','"+
birthDate+"');";
System.out.println(sql);
//使用StringBuffer拼接字符串
StringBuffer sbSql = new StringBuffer("insert into patient (patientName,gender,birthDate)" +
" values('");sbSql.append(patientName+"','");
sbSql.append(gender+"','");
sbSql.append(birthDate+"');");
sql = sbSql.toString();
System.out.println(sql);
- SQL语句中,字符
"
和'
是等效的- 但在Java代码中拼接字符串时使用字符
'
会使代码更加清晰- 也不容易出错引号、逗号或括号等符号必须成对出现
- 可在控制台输出拼接后的字符串,检查SQL语句是否正确
使用Statement接口执行插入数据的操作的方法
如果希望得到插入成功的数据行数,可以使用executeUpdate()方法;否则,使用execute()方法
实现步骤
演示案例
使用JDBC,向hospital数据库病人表中添加一个新的病人记录关键代码
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class HospitalInsert {
private static Logger logger = LogManager.getLogger(HospitalInsert.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
String name = "张菲";
// 姓名
String gender = "女";
// 性别
String birthDate = "1995-02-12";
// 出生日期
String phoneNum = "13887676500";
// 联系电话
String email = "[email protected]";
//邮箱
String password = "909000";
//密码
String identityNum = "610000199502126100";
//身份证号
String address = "北京市";
//地址
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "root");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
StringBuffer sbSql = new StringBuffer("insert into patient (patientName,gender,birthDate,phoneNum,email,password,identityNum,address) values ( '");
sbSql.append(name + "','");
sbSql.append(gender + "','");
sbSql.append(birthDate + "','");
sbSql.append(phoneNum + "','");
sbSql.append(email + "','");
sbSql.append(password + "','");
sbSql.append(identityNum + "','");
sbSql.append(address + "');");
System.out.println(sbSql.toString());
//3、执行插入操作
stmt.execute(sbSql.toString());
} catch (SQLException e) {
logger.error(e);
} finally {
// 4、关闭数据库连接
try {
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
为了避免可能出现的乱码问题,可将指定数据库连接的编码集为UTF8,多个参数间使用字符&进行分隔
jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8&useUnicode=true&characterEncoding=utf-8
实现步骤
需关注拼接的SQL字符串,以避免出错
演示案例
使用JDBC,将hospital数据库中patientID为13的病人电话更新为13627395833
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class HospitalUpdate {
private static Logger logger = LogManager.getLogger(HospitalUpdate.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
int patientID = 13;
// 病人编号
String phoneNum = "13627395833";
// 联系电话
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "root");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
StringBuffer sbSql = new StringBuffer("update patient ");
sbSql.append("set phoneNum='" + phoneNum + "' ");
sbSql.append("where patientID=" + patientID + ";");
System.out.println(sbSql.toString());
//3、执行插入更新操作
int effectRowNum = stmt.executeUpdate(sbSql.toString());
System.out.println("更新数据的行数:" + effectRowNum);
} catch (SQLException e) {
logger.error(e);
} finally {
// 4、关闭数据库连接
try {
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
保存和处理Statement执行后所产生的查询结果
方法 | 说明 |
---|---|
boolean next() | 将游标从当前位置向下移动一行 |
void close() | 关闭ResultSet 对象 |
int getInt(int colIndex) | 以int形式获取结果集当前行指定列号值 |
int getInt(String colLabel) | 以int形式获取结果集当前行指定列名值 |
float getFloat(int colIndex) | 以float形式获取结果集当前行指定列号值 |
float getFloat(String colLabel) | 以float形式获取结果集当前行指定列名值 |
String getString(int colIndex) | 以String形式获取结果集当前行指定列号值 |
String getString(String colLabel) | 以String形式获取结果集当前行指定列名值 |
- 要从中获取数据的列号或列名可作为方法的参数
- 根据值的类型选择对应的方法
ResultSet 接口 getXxx() 方法
int类型 -> getInt()
float类型 -> getFloat()
String类型 -> getString()
假设结果集的第一列为patientID,存储类型为int类型,能够获得该列值的两种方法
//使用列号提取数据
int id = rs.getInt(1);
//使用列名提取数据
int id = rs.getInt("patientID");
- 列号从1开始计数,与数组下标从0开始计数不同
- 采用列名来标识列可读性强,且不容易出错
使用 JDBC 从 hospital 数据库中查询前3个病人的编号、姓名、性别、住址信息并输出到控制台上
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
public class HospitalQuery {
private static Logger logger = LogManager.getLogger(HospitalQuery.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
int patientID = 13;
// 病人编号
String phoneNum = "13627395833";
// 联系电话
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "root");
System.out.println("建立连接成功 !");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
String sql = "select patientID,patientName,gender,address from patient limit 3;";
//3、执行查询更新操作
rs = stmt.executeQuery(sql);
//4、移动指针遍历结果集并输出查询结果
while (rs.next()) {
System.out.println(rs.getInt("patientID") + "\t" +
rs.getString("patientName") + "\t" +
rs.getString("gender") + "\t" +
rs.getString("address"));
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
通过提交一段SQL代码,执行超出用户访问权限的数据操作称为SQL注入(SQL Injection),SQL注入攻击是应用安全领域的一种常见攻击方式,会造成的数据库安全风险包括:刷库、拖库和撞库等,主要是没有对用户输入数据的合法性进行判断,导致应用程序存在安全隐患
使用JDBC实现医院管理系统用户登录验证功能
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.Scanner;
public class HospitalLogin {
private static Logger logger = LogManager.getLogger(HospitalLogin.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
String identityNum = input.next();
System.out.print("请输入密码:");
String password = input.next();
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "123456");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
StringBuffer sbSql = new StringBuffer("SELECT patientName FROM patient WHERE ");
sbSql.append("password='" + password + "'");
sbSql.append(" and identityNum='" + identityNum + "';");
//3、执行查询更新操作
rs = stmt.executeQuery(sbSql.toString());
System.out.println(sbSql.toString());
//4、验证用户名和密码
if (rs.next()) {
System.out.println("欢迎" + rs.getString("patientName") + "登录系统!");
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LWZU5FBo-1680617691979)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230404172438.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oFRlcayJ-1680617691982)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230404172502.png)]
修改查询结构
使用PreparedStatement 接口
PreparedStatement接口 (预编译的 SQL 语句)
提高代码可读性和可维护性
提高安全性
提高SQL语句执行的性能
方 法 | 作 用 |
---|---|
boolean execute() | 执行SQL语句,可以是任何SQL语句。如果结果是Result对象,则返回true。如果结果是更新计数或没有结果,则返回false |
ResultSet executeQuery() | 执行SQL查询,返回该查询生成的ResultSet对象 |
int executeUpdate() | 执行SQL语句,该语句必须是一个DML语句,比如:INSERT、UPDATE或DELETE语句;或者是无返回内容的SQL语句,比如DDL语句。返回值是执行该操作所影响的行数 |
void setXxx(int index,xxx x) | 方法名Xxx和第二个参数的xxx均表示(如int,float,double等)基本数据类型,且两个类型需一致,参数列表中的x表示方法的形式参数。把指定数据类型(xxx)的值x设置给index位置的参数。根据参数类型的不同,常见方法有:setInt(int index,int x) 、setFloat(int index,float x)、setDouble(int index,double x)等 |
void setObject(int index,Object x) | 除基本数据类型外,参数类型也可以是Object,可以将Object对象x设置给index位置的参数 |
创建PreparedStatement对象
设置输入参数的值
执行SQL语句
验证用户输入的身份证号和密码
- 如果通过验证,则输出“欢迎[姓名]登录系统!”的信息;
- 否则输出“密码错误!”
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.Scanner;
public class HospitalLogin {
private static Logger logger = LogManager.getLogger(HospitalLogin.class.getName());
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
String identityNum = input.next();
System.out.print("请输入密码:");
String password = input.next();
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "123456");
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT patientName FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
System.out.println("欢迎" + rs.getString("patientName") + "登录系统!");
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
实际开发中,推荐使用PreparedStatement接口执行数据库操作
使用JDBC技术访问数据库数据的关键代码
private String driver = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8";
private String user = “root";
private String password= “123456";
// 修改后需重新编译
Connection conn = null;
public Connection getConnection() {
if(conn==null) {
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {//省略代码……}
}
return conn;// 返回连接对象
}
让用户脱离程序本身修改相关的变量设置——使用配置文件
Java的配置文件常为properties文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vLwQh0OR-1680617691982)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230404204942.png)]
通常,为数据库访问添加的配置文件是database.properties
使用java.util包下的Properties类读取配置文件
方法 | 描述 |
---|---|
String getProperty(String key) | 用指定的键在此属性列表中搜索属性,通过参数key得到其所对应的值 |
Object setProperty(String key, String value) | 通过调用基类Hashtable的put()方法设置键-值对 |
void load(InputStream inStream) | 从输入流中读取属性列表 (键和元素对),通过对指定文件进行装载获取该文件中所有键-值对 |
void clear() | 清除所装载的键-值对,该方法由基类Hashtable提供 |
使用Properties配置文件的方式改造医院管理系统
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class HospitalSystem {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String patientID = null;
boolean isExist = false;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
String identityNum = input.next();
System.out.print("请输入密码:");
String password = input.next();
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
String url = null;
String username = null;
String pwd = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
pwd = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, pwd);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT patientID, patientName FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
patientID = rs.getString("patientID");
System.out.println("欢迎" + rs.getString("patientName") + "登录系统!");
while (!isExist) {
System.out.println("1.查询检查记录\t 0.退出");
System.out.print("请输入要执行的操作:");
String action = input.next();
if (action.equals("1")) {
pstmt = conn.prepareStatement("SELECT depName, checkItemName, checkResult, checkItemCost, examDate FROM prescription p INNER JOIN department d ON p.depID = d.depID INNER JOIN checkitem c ON p.checkItemID = c.checkItemID WHERE p.patientID=?;");
pstmt.setString(1, patientID);
rs = pstmt.executeQuery();
System.out.println("检查科室\t检查项目\t检查结果\t检查费用\t检查时间");
while (rs.next()) {
System.out.println(rs.getString("depName") + "\t" + rs.getString("checkItemName") + "\t" + rs.getString("checkResult") + "\t" + rs.getString("checkItemCost") + "\t" + rs.getString("examDate") + "\t");
}
} else if (action.equals("0")) {
isExist = true;
System.out.println("再见");
} else {
System.out.println("输入错误,请重新输入");
}
}
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
非常流行的数据访问模式——DAO模式
DAO起着转换器的作用,把实体类转换为数据库中的记录
组成部分
优势
数据访问代码和业务逻辑代码之间通过实体类来传输数据
实体类特征
实体类(Entity)是Java应用程序中与数据库表对应的类
package XaunZiShare;
import java.io.Serializable;
public class Patient implements Serializable {
private static final long serialVersionUID = -8762235641468472877L;
private String patientID; //病人编号
private String password; //登录密码
private String birthDate; //出生日期
private String gender; //性别
private String patientName; //姓名
private String phoneNum; //联系电话
private String email; //邮箱
private String identityNum; //身份证号
private String address; //地址
/**
* 无参构造方法
*/
public Patient() {
}
/**
* 有参构造方法,根据需要提供
*
* @param identityNum 身份证号
* @param name 姓名
*/
public Patient(String identityNum, String name) {
this.identityNum = identityNum;
this.patientName = name;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public String getPatientID() {
return patientID;
}
public void setPatientID(String patientID) {
this.patientID = patientID;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String 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;
}
}
package XaunZiShare;
import com.javamysql.entity.Patient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class HospitalSystem {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Patient patient = new Patient();
boolean isExist = false;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
patient.setIdentityNum(input.next());
System.out.print("请输入密码:");
patient.setPassword(input.next());
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
String url = null;
String username = null;
String pwd = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
pwd = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, pwd);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT * FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, patient.getIdentityNum());
pstmt.setString(2, patient.getPassword());
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
//从MySQL读取用户信息,并加载到patient对象中
patient.setPatientID(rs.getString("patientID"));
patient.setAddress(rs.getString("address"));
patient.setBirthDate(rs.getString("birthDate"));
patient.setEmail(rs.getString("email"));
patient.setGender(rs.getString("gender"));
patient.setPatientID(rs.getString("patientName"));
patient.setPhoneNum(rs.getString("phoneNum"));
System.out.println("欢迎" + patient.getPatientName() + "登录系统!");
while (!isExist) {
System.out.println("1.查询检查记录\t2.查询病人信息\t 0.退出");
System.out.print("请输入要执行的操作:");
String action = input.next();
if (action.equals("1")) {
pstmt = conn.prepareStatement("SELECT depName, checkItemName, checkResult, checkItemCost, examDate FROM prescription p INNER JOIN department d ON p.depID = d.depID INNER JOIN checkitem c ON p.checkItemID = c.checkItemID WHERE p.patientID=?;");
pstmt.setString(1, patient.getPatientID());
rs = pstmt.executeQuery();
System.out.println("检查科室\t检查项目\t检查结果\t检查费用\t检查时间");
while (rs.next()) {
System.out.println(rs.getString("depName") + "\t" + rs.getString("checkItemName") + "\t" + rs.getString("checkResult") + "\t" + rs.getString("checkItemCost") + "\t" + rs.getString("examDate") + "\t");
}
} else if (action.equals("2")) {
System.out.println(patient.getPatientID() + "\t" + patient.getPatientName() + "\t" + patient.getGender() + "\t" + patient.getBirthDate() + "\t" + patient.getIdentityNum() + "\t" + patient.getPhoneNum() + "\t" + patient.getEmail() + "\t" + patient.getAddress());
} else if (action.equals("0")) {
isExist = true;
System.out.println("再见");
} else {
System.out.println("输入错误,请重新输入");
}
}
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
实体类特征
如果实体类实现了java.io.Serializable接口,应该定义属性serialVersionUID,解决不同版本之间的序列化问题
为serialVersionUID赋值的方法
手动
使用IDEA生成
private static final long serialVersionUID = -8762235641468472877L;
一旦为一个实体类的serialVersionUID赋值,就不要再修改;否则,在反序列化之前版本的数据时,会报java.io.InvalidClassException异常
将程序中数据在瞬时状态和持久状态间转换的机制为数据持久化
JDBC
数据库
普通文件
XML文件
Scanner input = new Scanner(System.in);
System.out.print("请输入登录名:");
String name=input.next();
System.out.print("请输入登录密码:");
String password=input.next();
// 业务相关代码
// ……省略加载驱动
try {
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8",
"root", "123456");
// … …省略代码 … …
if(rs.next())
System.out.println("登录成功,欢迎您!");
else
System.out.println("登录失败,请重新输入!");
// ……省略代码
} catch (SQLException e) {
// ……省略代码
} finally {}
// 数据访问代码
业务代码和数据访问代码耦合
采用面向接口编程,可以降低代码间的耦合性
采用面向接口编程,可以降低代码间的耦合性
业务逻辑代码调用数据访问接口
将HospitalSystem中对病人的所有数据库操作抽象成接口
对病人的数据库操作包括修改病人信息、通过身份证号和密码验证登录
设计接口时,尽量以对象为单位,给调用者提供面向对象的接口
接口的命名,应以简明为主
接口由不同数据库的实现类分别实现
PatientDao 接口
package XaunZiShare;
import com.javamysql.entity.Patient;
public interface PatientDao {
/**
* 更新病人信息
*
* @param patient 病人
*/
int update(Patient patient);
/**
* 根据身份证号和登录密码返回病人信息
*
* @param identityNum 身份证号
* @param pwd 登录密码
* @return 病人
*/
Patient getPatientByIdNumAndPwd(String identityNum, String pwd);
}
PatientDao实现类的方法:update()方法
package XaunZiShare;
import com.javamysql.HospitalSystem;
import com.javamysql.dao.PatientDao;
import com.javamysql.entity.Patient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class PatientDaoMySQLImpl implements PatientDao {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
@Override
public int update(Patient patient) {
Connection conn = null;
PreparedStatement pstmt = null;
int result = 0;
Properties params = new Properties();
String configFile = "database.properties";//配置文件路径
String url = null;
String username = null;
String password = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
password = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, password);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("UPDATE patient SET address=?, birthDate=?, email=?, gender=?, patientName=?, phoneNum=?, identityNum=?,password=? WHERE patientID=?");
pstmt.setString(1, patient.getAddress());
pstmt.setString(2, patient.getBirthDate());
pstmt.setString(3, patient.getEmail());
pstmt.setString(4, patient.getGender());
pstmt.setString(5, patient.getPatientName());
pstmt.setString(6, patient.getPhoneNum());
pstmt.setString(7, patient.getIdentityNum());
pstmt.setString(8, patient.getPassword());
pstmt.setString(9, patient.getPatientID());
result = pstmt.executeUpdate();
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
return result;
}
@Override
public Patient getPatientByIdNumAndPwd(String identityNum, String pwd) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Patient patient = null;
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
String url = null;
String username = null;
String password = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
password = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, password);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT * FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, pwd);
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
//从MySQL读取用户信息,并加载到patient对象中
patient = new Patient();
patient.setPatientID(rs.getString("patientID"));
patient.setAddress(rs.getString("address"));
patient.setBirthDate(rs.getString("birthDate"));
patient.setEmail(rs.getString("email"));
patient.setGender(rs.getString("gender"));
patient.setPatientName(rs.getString("patientName"));
patient.setPhoneNum(rs.getString("phoneNum"));
patient.setIdentityNum(rs.getString("identityNum"));
patient.setPassword(rs.getString("password"));
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
return patient;
}
}
通用的操作是否能够进一步简化?
package XaunZiShare;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class BaseDao {
private static String driver;
// 数据库驱动字符串
private static String url;
// 连接URL字符串
private static String user;
// 数据库用户名
private static String password;
// 用户密码
// 数据连接对象
static {//静态代码块,在类加载的时候执行
init();
}
Connection conn = null;
/**
* 初始化连接参数,从配置文件里获得
*/
public static void init() {
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
//加载配置文件到输入流中
InputStream is = BaseDao.class.getClassLoader().getResourceAsStream(configFile);
try {
//从输入流中读取属性列表
params.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//根据指定的获取对应的值
driver = params.getProperty("driver");
url = params.getProperty("url");
user = params.getProperty("username");
password = params.getProperty("password");
}
/**
* 获取数据库连接对象
*/
public Connection getConnection() {
try {
if (conn == null || conn.isClosed()) {
// 获取连接并捕获异常
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
// 异常处理
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
// 返回连接对象
}
/**
* 关闭数据库连接
*
* @param conn 数据库连接
* @param stmt Statement对象
* @param rs 结果集
*/
public void closeAll(Connection conn, Statement stmt, ResultSet rs) {
// 若结果集对象不为空,则关闭
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 若Statement对象不为空,则关闭
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 若数据库连接对象不为空,则关闭
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 增、删、改的操作
*
* @param preparedSql 预编译的 SQL 语句
* @param param 参数的字符串数组
* @return 影响的行数
*/
public int exceuteUpdate(String preparedSql, Object[] param) {
PreparedStatement pstmt = null;
int num = 0;
conn = getConnection();
try {
pstmt = conn.prepareStatement(preparedSql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
//为预编译sql设置参数
pstmt.setObject(i + 1, param[i]);
}
}
num = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(conn, pstmt, null);
}
return num;
}
package XaunZiShare;
import com.javamysql.HospitalSystem;
import com.javamysql.dao.BaseDao;
import com.javamysql.dao.PatientDao;
import com.javamysql.entity.Patient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class PatientDaoMySQLImpl extends BaseDao implements PatientDao {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
@Override
public int update(Patient patient) {
//构造SQL语句
String preparedSQL = "UPDATE patient SET address=?, birthDate=?, email=?, gender=?, patientName=?, phoneNum=?, identityNum=?,password=? WHERE patientID=?";
//构造SQL执行参数数组
List<String> params = new ArrayList<String>();
params.add(patient.getAddress());
params.add(patient.getBirthDate());
params.add(patient.getEmail());
params.add(patient.getGender());
params.add(patient.getPatientName());
params.add(patient.getPhoneNum());
params.add(patient.getIdentityNum());
params.add(patient.getPassword());
params.add(patient.getPatientID());
//调用BaseDao中的更新
return exceuteUpdate(preparedSQL, params.toArray());
}
@Override
public Patient getPatientByIdNumAndPwd(String identityNum, String pwd) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Patient patient = null;
conn = getConnection();
try {
//构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT * FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, pwd);
rs = pstmt.executeQuery();
//验证用户名和密码
if (rs.next()) {
//从MySQL读取用户信息,并加载到patient对象中
patient = new Patient();
patient.setPatientID(rs.getString("patientID"));
patient.setAddress(rs.getString("address"));
patient.setBirthDate(rs.getString("birthDate"));
patient.setEmail(rs.getString("email"));
patient.setGender(rs.getString("gender"));
patient.setPatientName(rs.getString("patientName"));
patient.setPhoneNum(rs.getString("phoneNum"));
patient.setIdentityNum(rs.getString("identityNum"));
patient.setPassword(rs.getString("password"));
}
} catch (SQLException e) {
logger.error(e);
} finally {
//关闭数据库连接
closeAll(conn, pstmt, rs);
}
return patient;
}
}
此种封装JDBC的模式称为DAO模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6RO8XGh-1680617691983)(./assets/SRC.png)]
项目名其实就是反过来的网站网址
这个包里面放的是都是,数据表的实体类,(POJO)。即具有,基础属性,属性封装(getter/setter),构造函数,和Tostring()的普通类
dao 包里面还有一层包叫``impl即实现类,dao 包下的接口类只定义方法,具体实现由
impl`包下的实现类实现
service 为业务层,将基础实现类,整合为复杂业务,与dao 包一样包内部还有一层包impl
service 包下的接口类只定义方法,具体实现由impl
包下的实现类实现
代码运行类
项目所需的 JDBC 驱动 放置在此包内
项目连接数据库所需要的配置文件
直接参考阿里巴巴发布的《阿里巴巴Java开发手册(终极版)v1.3版本》
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YTAOLKOH-1680617691983)(./assets/image-20230404213509656.png)]