Java数据库连接基础JDBC

文章目录

  • JDBC简介
  • JDBC体系结构
  • JDBC核心组件
  • 使用步骤
  • 建立连接
  • 单表操作
    • Statement
      • 查询操作
      • 增删改操作
    • ResultSet
  • sql注入
    • 简介
    • 避免sql注入
  • 多表操作
    • 一对多
    • 多对一
    • 一对一
    • 多对多


JDBC简介

Java Database Connectivity,简称JDBC,Java数据库连接,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法

数据库的相关内容及操作可参考:
sql简介及三大范式
sql语言及基本操作
SQL完整性、多表查询及事务


JDBC体系结构

JDBC API支持用于数据库访问的两层和三层处理模型

JDBC API使用驱动程序管理器和特定于数据库的驱动程序来提供与异构数据库的透明连接

JDBC体系结构通常由两层组成

  • JDBC API:提供应用程序到JDBC管理器连接
  • JDBC驱动程序API:支持JDBC管理器到驱动程序连接

JDBC核心组件

名称描述
DriverManager此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配
Driver此接口处理与数据库服务器的通信。通常很少会直接与Driver对象进行交互,而是使用DriverManager对象来管理这种类型的对象
Connection该界面具有用于联系数据库的所有方法,连接对象表示通信上下文
Statement使用从此接口创建的对象将SQL语句提交到数据库,除了执行存储过程之外一些派生接口还接受参数
ResultSet在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据,作为一个迭代器,允许我们移动其数据
SQLException此类处理数据库应用程序中发生的任何错误

使用步骤

  • 导入包
    需要包含包含数据库编程所需的JDBC类的包
    (大多数情况下,使用import java.sql.*)
  • 注册JDBC驱动程序
    初始化驱动程序,以便可以打开与数据库的通信通道
  • 打开连接
    使用DriverManager.getConnection()方法创建一个Connection对象,表示与数据库的物理连接
  • 执行查询
    使用类型为Statement的对象来构建和提交SQL语句到数据库
  • 从结果集中提取数据
    使用相应的ResultSet.getXXX()方法从结果集中检索数据
  • 释放资源
    关闭所有数据库资源,而不依赖于JVM的GC

建立连接

用到的核心组件

名称描述
DriverManager此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配
Driver此接口处理与数据库服务器的通信。通常很少会直接与Driver对象进行交互,而是使用DriverManager对象来管理这种类型的对象
Connection该界面具有用于联系数据库的所有方法,连接对象表示通信上下文

1、 导入JDBC包

import java.sql.*;

2、 注册JDBC驱动程序
使JVM将所需的驱动程序实现加载到内存中,以便满足JDBC请求

RDBMSJDBC驱动程序名称网址格式
MYSQL8com.mysql.cj.jdbc.Driverjdbc:mysql://hostname:3306/databaseName?serverTimezone=UTC
MySQLcom.mysql.jdbc.Driverjdbc:mysql://hostname:3306/databaseName
ORACLEoracle.jdbc.driver.OracleDriverjdbc:oracle:thin:@hostname:port Number:databaseName
DB2COM.ibm.db2.jdbc.DB2Driverjdbc:db2:hostname:port Number / databaseName
SYBASEcom.sybase.jdbc.SybDriverjdbc:sybase:Tds:hostname:port Number / databaseName
  • 方法一:Class.forName();
    注册驱动程序最常见的方法是使用Java的Class.forName()方法,将驱动程序的类文件动态加载到内存中,并将其自动注册
Class.forName("com.mysql.cj.jdbc.Driver");
  • 方法二:DriverManager.registerDriver();
    使用静态DriverManager.registerDriver()方法
Driver myDriver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver( myDriver );

3、 数据库URL配置

创建一个格式正确的地址,指向要连接到的数据库mydb010402

String url = "jdbc:mysql://localhost:3306/mydb010402?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";

链接地址一般有两种:

"jdbc:mysql://localhost:3306/数据库名?useSSL=false&useUnicode=true&characterEncoding=UTF-8"or"jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC"

4、 创建连接对象

加载驱动程序后,使用DriverManager.getConnection()方法建立数据库连接

  • 方法一
    getConnection(String url)
  • 方法二
    getConnection(String url,Properties prop)
  • 方法三
    getConnection(String url,String user,String password)
String userName = "root";
String passWord = "123456";
Connection connection = DriverManager.getConnection(url, userName, passWord);
  • 方法四
    使用数据库URL和属性对象
    DriverManager.getConnection(String url, Properties info);
import java.util.*;
String URL = "jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC";
Properties info = new Properties( );
info.put( "user", "username" );
info.put( "password", "password" );
Connection connection = DriverManager.getConnection(URL, info);

5、 关闭数据库
为确保连接关闭,可以在代码中提供一个“finally”块

if(connection != null){connection.close();
}

单表操作

用到的核心组件

名称描述
Statement使用从此接口创建的对象将SQL语句提交到数据库,除了执行存储过程之外一些派生接口还接受参数
ResultSet在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据,作为一个迭代器,允许我们移动其数据
SQLException此类处理数据库应用程序中发生的任何错误

Statement

执行方法描述
boolean execute(String SQL)检索到ResultSet对象返回true; 否则返回false【执行SQL DDL语句或需要使用真正的动态SQL时使用】
int executeUpdate(String SQL)返回受SQL语句执行影响的行数【使用此方法执行预期会影响多个行的SQL语句,例如INSERT,UPDATE或DELETE语句】
ResultSet executeQuery(String SQL)返回一个ResultSet对象【当希望获得结果集时,使用此方法(像使用SELECT语句一样)】

连接sql后,创建状态通道(进行sql语句的发送)

Statement statement = connection.createStatement();

查询操作

//执行查询
resultSet = statement.executeQuery("select * from employee");
//取出结果集信息
while (resultSet.next()) {//判断是否有下一条数据,类似于迭代器//取出数据:resultSet.getXXX("列名");XXX表示数据类型System.out.println("姓名:" + resultSet.getString("name") + ",生日:" + resultSet.getDate("birthday"));
}

连接sql及执行查询完整代码如下

class Demo {public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/mydb010402?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建状态通道(进行sql语句的发送)statement = connection.createStatement();//执行查询resultSet = statement.executeQuery("select * from employee");//3取出结果集信息while (resultSet.next()) {//判断是否有下一条数据,类似于迭代器//取出数据:resultSet.getXXX("列名");XXX表示数据类型System.out.println("姓名:" + resultSet.getString("name") + ",生日:" + resultSet.getDate("birthday"));}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {//5关闭资源try {if(resultSet != null){resultSet.close();}if(statement != null){statement.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finally}//end main
}//end class

增删改操作

//执行增删改查
int result = statement.executeUpdate("insert into employee(empid,name,birthday) values(2222,'二哈','2000-1-1')");
//返回结果为受影响的行数
if(result > 0){System.out.println("执行成功");
}else{System.out.println("执行失败");
}

连接sql及执行增删改完整代码如下

class Demo {public static void main(String[] args) {Connection connection = null;Statement statement = null;try {//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/mydb010402?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建状态通道(进行sql语句的发送)statement = connection.createStatement();//执行增删改查int result = statement.executeUpdate("insert into employee(empid,name,birthday) values(2222,'二哈','2000-1-1')");//返回结果为受影响的行数if(result > 0){System.out.println("执行成功");}else{System.out.println("执行失败");}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {//5关闭资源try {    if(statement != null){statement.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finally}//end main
}//end class

关闭Statement对象

为确保连接关闭,可以在代码中提供一个“finally”块

if(statement != null){statement.close();
}

ResultSet

ResultSet对象维护指向结果集中当前行的游标(结果集:包含在ResultSet对象中的行和列数据)

一般只需掌握以下两种即可:

类型描述
resultSet.getXXX(“列名”);取出数据,XXX表示数据类型
resultSet.next();判断是否有下一行数据

扩展:

类型描述
ResultSet.TYPE_SCROLL_INSENSITIVE光标可以向前和向后滚动,结果集对创建结果集后发生的数据库的其他更改不敏感
ResultSet.TYPE_SCROLL_SENSITIVE光标可以向前和向后滚动,结果集对创建结果集之后发生的其他数据库所做的更改敏感。
ResultSet.TYPE_FORWARD_ONLY光标只能在结果集中向前移动。

如果没有指定任何ResultSet类型,自动获得TYPE_FORWARD_ONLY


sql注入

简介

sql注入攻击是通过将恶意的Sql查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,是目前黑客对数据库进行攻击最常用手段之一

代码示例:
字符串username和password连接到sql后得到查询语句select * from users where username = “admin” and password= “abc” or 1=1
不管前面是否查询到数据,后面1=1恒为true,因此当通过此来查询用户名和密码进行登录时永远能登录成功

String username ="admin";
String password=" 'abc' or 1=1 ";
String sql="select * from users where username= '"+username+"' and password="+password;

此为使用Statement通道时存在的sql注入问题,避免sql注入可使用PreparedStatement(预状态通道)


避免sql注入

PreparedStatement接口扩展了Statement接口,提供了一个通用的Statement对象,动态地提供参数

StatementPreparedStatement
属于状态通道属于预状态通道
执行语句的时候进行编译先编译sql语句,再去执行,执行效率更高
不支持占位符支持占位符? ,给占位符赋值的时候,位置从1开始
存在sql注入问题可以防止sql注入(在处理值时以字符串的方式处理)

占位符:?
先占位,然后通过setXXX(index,vavle)方法对占位符进行赋值(XXX为数据类型,index从1开始)

代码示例:

			//定义sql,创建预状态通道(进行sql语句的发送)String sql = "select* from users where userName = ? and password = ? ";PreparedStatement pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始String uname = input.nextLine();String upass = input.nextLine();pps.setString(1,uname);pps.setString(2,upass);//执行sqlResultSet resultSet = pps.executeQuery();//返回结果为受影响的行数if(resultSet.next()){System.out.println("登录成功");}else{System.out.println("登录失败");}

关闭PreparedStatement对象

为确保连接关闭,可以在代码中提供一个“finally”块

if(pps != null){pps.close();
}

多表操作

多表操作首先要建立多表关系,再进行数据处理

两表操作有以下四种关系:
双向一对一
一对多
多对一
多对多

建立两表关系:
数据库通过外键建立两表关系
实体类通过属性的方式建立两表关系

实体类要求:
类名=表名
列名=属性名


以学生和老师为例,首先新建表student和teacher


根据表student和teacher在java中新建对应的类
类名=表名
列名=属性名

Student

public class Student {private int stuid;private String stuname;//外键列一般不生成属性public int getStuId() {return stuid;}public void setStuId(int stuid) {this.stuid = stuid;}public String getStuName() {return stuname;}public void setStuName(String stuname) {this.stuname = stuname;}
}

Teacher

public class Teacher {private int tid;private String tname;public int getTid() {return tid;}public void setTid(int tid) {this.tid = tid;}public String getTname() {return tname;}public void setTname(String tname) {this.tname = tname;}
}

一对多

一对多是在一方创建存储多方数据的集合,因此在teacher类中创建list存储多方(student)的数据,并添加set和get方法

    private List<Student> list = new ArrayList<Student>();//由于一位老师对应多位学生,因此学生用集合存储public List<Student> getList() {return list;}public void setList(List<Student> list) {this.list = list;}

一位老师对应多位学生,因此新建数据处理的接口TeacherDao,定义抽象操作方法

public interface TeacherDao {//定义操作方法//定义一个根据老师id查询老师信息(学生的信息)Teacher getById(int tid);
}

TeacherDaoImplements类实现TeacherDao接口,编写具体方法

public class TeacherDaoImplements implements TeacherDao {@Overridepublic Teacher getById(int tid) {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;Teacher teacher = new Teacher();try {//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建预状态通道(进行sql语句的发送)String sql = "select * from student s,teacher t where s.teacherid=t.tid and t.tid=?";pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始pps.setInt(1,tid);//执行sqlresultSet = pps.executeQuery();List<Student> students = new ArrayList<Student>();//老师→学生,存储学生信息,再add到老师while(resultSet.next()){//1.取出各自的信息teacher.setTid(resultSet.getInt("tid"));teacher.setTname(resultSet.getString("tname"));Student student = new Student();student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));//2.建立学生和老师之间的关系students.add(student);//每次将学生信息add到集合}teacher.setList(students);//set学生信息到teacher} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//5关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn teacher;//返回teacher}
}

最后编写操作实现类Test

public class Test {public static void main(String[] args) {TeacherDao dao = new TeacherDaoImplements();Teacher teacher = dao.getById(1);//查询id为1的老师及其学生System.out.println("teacher:" + teacher.getTname());//老师为一,因此只需输出一次List<Student> studentList = teacher.getList();//学生为多,因此集合存储,循环遍历输出for (Student student : studentList) {System.out.println("\t studentname=" + student.getStuName());}}
}

运行结果:

teacher:张三老师studentname=bbstudentname=ddstudentname=ee

sql查询结果:


多对一

与一对多不同,一对多是在一方创建存储多方数据的集合,多对一是在多方创建存储一方数据的对象

因此先在多方的学生类student中创建存储老师的对象,并添加set和get方法

    private Teacher teacher;public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}

多位学生对应一位老师,因此新建数据处理的接口Dao,定义抽象操作方法

public interface Dao {//定义操作方法//查询所有学生,学生中包含老师的信息public List<Student> getAll();
}

DaoImplements类实现Dao接口,编写具体方法

public class DaoImplements implements Dao {@Overridepublic List<Student> getAll() {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;List<Student> students = new ArrayList<>();//创建学生集合try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "select * from student s,teacher t where s.teacherid=t.tid";pps = connection.prepareStatement(sql);//执行sqlresultSet = pps.executeQuery();//取值while(resultSet.next()){Student student = new Student();student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));Teacher teacher = new Teacher();teacher.setTid(resultSet.getInt("tid"));teacher.setTname(resultSet.getString("tname"));//建立学生和老师之间的关系student.setTeacher(teacher);students.add(student);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn students;}//end method
}//end class

最后编写操作实现类Test

public class Test{public static void main(String[] args) {Dao dao = new DaoImplements();List<Student> students = dao.getAll();for (Student student : students) {System.out.println(student.getStuName()+","+student.getTeacher().getTname());}}
}

运行结果:

aaa,王五
bb,张三老师
cc,王五
dd,张三老师
ee,张三老师
ff,李四老师

sql查询结果:


一对一

以夫妻为例,首先新建表Husband和Wife

根据表Husband和Wife在java中新建对应的类
类名=表名
列名=属性名

  • Husband
public class Husband {private int husid;private String husname;public int getHusId() {return husid;}public void setHusId(int husid) {this.husid = husid;}public String getHusName() {return husname;}public void setHusName(String husname) {this.husname = husname;}
}
  • Wife
public class Wife {private int wifeid;private String wifeName;public int getWifeId() {return wifeid;}public void setWifeId(int wifeid) {this.wifeid = wifeid;}public String getWifeName() {return wifeName;}public void setWifeName(String wifeName) {this.wifeName = wifeName;}
}

一对一是在一方创建存储多方数据的集合,因此在husband和wife类中分别创建wife和husband,并添加set和get方法

  • husband中添加对应的wife信息
	private Wife wife;//一对一,一位丈夫对应一位妻子//获取和设置妻子的信息public Wife getWife() {return wife;}public void setWife(Wife wife) {this.wife = wife;}
  • wife中添加对应的husband信息
    private Husband husband;//一对一,一位妻子对应一位丈夫//获取和设置丈夫的信息public Husband getHusband() {return husband;}public void setHusband(Husband husband) {this.husband = husband;}

一位丈夫对应一位妻子,因此新建数据处理的接口Dao,定义抽象操作方法

public interface Dao {//查询妻子信息(包含丈夫信息)public Wife getWife(int wid);//查询丈夫信息(包含妻子信息)public Husband getHus(int hid);
}

DaoImplements类实现Dao接口,编写具体方法

  • 丈夫与妻子建立关系getWife
    @Overridepublic Wife getWife(int wid) {//连接sqlConnection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;List<Student> students = new ArrayList<>();//创建学生集合try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "SELECT* from wife w,husband h where w.hid = h.husid and w.wifeid = ?;";pps = connection.prepareStatement(sql);pps.setInt(1, wid);//执行sqlresultSet = pps.executeQuery();//取值Wife wife = new Wife();while (resultSet.next()) {//取出各自的信息wife.setWifeId(resultSet.getInt("wifeid"));wife.setWifeName(resultSet.getString("wifename"));Husband husband = new Husband();husband.setHusId(resultSet.getInt("husid"));husband.setHusName(resultSet.getString("husname"));//建立夫妻关系,丈夫与妻子建立关系wife.setHusband(husband);}return wife;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();} finally {try {//关闭资源if (resultSet != null) {resultSet.close();}if (pps != null) {pps.close();}if (connection != null) {connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}
  • 妻子与丈夫建立关系getHus
	@Overridepublic Husband getHus(int hid) {//连接sqlConnection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;List<Student> students = new ArrayList<>();//创建学生集合try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "SELECT* from wife w,husband h where w.hid = h.husid and h.husid = ?;";pps = connection.prepareStatement(sql);pps.setInt(1, hid);//执行sqlresultSet = pps.executeQuery();//取值Husband husband = new Husband();while (resultSet.next()) {//取出各自的信息husband.setHusId(resultSet.getInt("husid"));husband.setHusName(resultSet.getString("husname"));Wife wife = new Wife();wife.setWifeId(resultSet.getInt("wifeid"));wife.setWifeName(resultSet.getString("wifename"));//建立夫妻关系,妻子与丈夫建立关系husband.setWife(wife);}return husband;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();} finally {try {//5关闭资源if (resultSet != null) {resultSet.close();}if (pps != null) {pps.close();}if (connection != null) {connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}

最后编写操作实现类Test

public class Test{public static void main(String[] args) {DaoImplements dao = new DaoImplements();Wife wife = dao.getWife(1);System.out.println("wife:" + wife.getWifeName() + ", husband:" + wife.getHusband().getHusName());Husband hus = dao.getHus(2);System.out.println("husband:" + hus.getHusName() + ", wife:" + hus.getWife().getWifeName());}
}

运行结果:

wife:aa, husband:AA
husband:BB, wife:bb

sql查询结果:


多对多

多对多建表原则:
除了对应关系的两张表外,需要创建第三张表,该表至少两个字段,两个字段分别作为外键指向各自一方的主键

以课程和学生为例,多门课程对应一位学生,一位学生对应多门课程,因此课程与学生的关系为多对多

首先建立两张表subject和student


再建立subject和student的中间表middle,middle中除了自身的主键外,另外两个字段分别为subject和student的主键

根据表Subject和Student在java中新建对应的类
类名=表名
列名=属性名

  • Subject
public class Subject {private int subid;private String subname;public int getSubid() {return subid;}public void setSubid(int subid) {this.subid = subid;}public String getSubName() {return subname;}public void setSubName(String subname) {this.subname = subname;}
}
  • Student
public class Student {private int stuid;private String stuname;public int getStuId() {return stuid;}public void setStuId(int stuid) {this.stuid = stuid;}public String getStuName() {return stuname;}public void setStuName(String stuname) {this.stuname = stuname;}
}

多对多,即两方分别创建存储另一方数据的集合

因此先在多方的课程类subject和学生类student中分别创建存储对方的集合,并添加set和get方法

  • Subject中添加对应的学生信息
	private List stulist;//多对多,因此课程中存储student集合public List getStuList() {return stulist;}public void setStuList(List stulist) {this.stulist = stulist;}
  • Student中添加对应的课程信息
	private List<Subject> subjects;//多对多,因此学生中存储subject集合public List<Subject> getSubjects() {return subjects;}public void setSubjects(List<Subject> subjects) {this.subjects = subjects;}

多门课程对应多位学生,因此新建数据处理的接口Dao,定义抽象操作方法

public interface Dao {//查询某个学生信息(查询出所学科目)public Student findByStuId(int stuId);//查询某个科目以及对应的学生姓名public Subject findBySubId(int subId);
}

DaoImplements类实现Dao接口,编写具体方法

  • 学生 与课程建立关系findBySubId
	@Overridepublic Subject findBySubId(int subId) {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "select* from student st,subject su,middle m where st.stuid = m.stuid and su.subid = m.subid and su.subid = ?";pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始pps.setInt(1,subId);//执行sqlresultSet = pps.executeQuery();Subject subject = new Subject();List<Student> students = new ArrayList<>();while(resultSet.next()){//取出各自的信息subject.setSubid(resultSet.getInt("subid"));subject.setSubName(resultSet.getString("subname"));Student student = new Student();student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));students.add(student);}//建立student和subject之间的关系,subject→studentsubject.setStuList(students);//set学生信息到subjectreturn subject;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}
  • 课程 与学生建立关系findByStuId
	@Overridepublic Student findByStuId(int id) {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建预状态通道(进行sql语句的发送)String sql = "select* from student st,subject su,middle m where st.stuid = m.stuid and su.subid = m.subid and st.stuid = ?";pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始pps.setInt(1,stuId);//执行sqlresultSet = pps.executeQuery();Student student = new Student();List<Subject> subjects = new ArrayList<>();while(resultSet.next()){//取出各自的信息student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));Subject subject = new Subject();subject.setSubid(resultSet.getInt("subid"));subject.setSubName(resultSet.getString("subname"));subjects.add(subject);}//建立subject和student之间的关系,student→subjectstudent.setSubjects(subjects);//set科目信息到studentreturn student;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}

最后编写操作实现类Test

public class Test{public static void main(String[] args) {DaoImplements dao = new DaoImplements();//通过课程找学生Subject subject1 = dao.findBySubId(2);System.out.println("课程名称:" + subject1.getSubName());List<Student> studentList = subject1.getStuList();System.out.println("学习该课程的学生:");for (Student student1 : studentList) {System.out.println("\t"+student1.getStuName());}//通过学生找课程Student student2 = dao.findById(1);System.out.println("学生姓名:" + student2.getStuName());List<Subject> subjects = student2.getSubjects();System.out.println("学习的课程:");for (Subject subject2 : subjects) {System.out.println("\t"+subject2.getSubName());}}//end main
}//end class

运行结果:

课程名称:ui
学习该课程的学生:张三李四王五赵六花花潇潇
学生姓名:张三
学习的课程:javauih5c++

sql查询结果:


Java数据库连接基础JDBC

文章目录

  • JDBC简介
  • JDBC体系结构
  • JDBC核心组件
  • 使用步骤
  • 建立连接
  • 单表操作
    • Statement
      • 查询操作
      • 增删改操作
    • ResultSet
  • sql注入
    • 简介
    • 避免sql注入
  • 多表操作
    • 一对多
    • 多对一
    • 一对一
    • 多对多


JDBC简介

Java Database Connectivity,简称JDBC,Java数据库连接,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法

数据库的相关内容及操作可参考:
sql简介及三大范式
sql语言及基本操作
SQL完整性、多表查询及事务


JDBC体系结构

JDBC API支持用于数据库访问的两层和三层处理模型

JDBC API使用驱动程序管理器和特定于数据库的驱动程序来提供与异构数据库的透明连接

JDBC体系结构通常由两层组成

  • JDBC API:提供应用程序到JDBC管理器连接
  • JDBC驱动程序API:支持JDBC管理器到驱动程序连接

JDBC核心组件

名称描述
DriverManager此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配
Driver此接口处理与数据库服务器的通信。通常很少会直接与Driver对象进行交互,而是使用DriverManager对象来管理这种类型的对象
Connection该界面具有用于联系数据库的所有方法,连接对象表示通信上下文
Statement使用从此接口创建的对象将SQL语句提交到数据库,除了执行存储过程之外一些派生接口还接受参数
ResultSet在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据,作为一个迭代器,允许我们移动其数据
SQLException此类处理数据库应用程序中发生的任何错误

使用步骤

  • 导入包
    需要包含包含数据库编程所需的JDBC类的包
    (大多数情况下,使用import java.sql.*)
  • 注册JDBC驱动程序
    初始化驱动程序,以便可以打开与数据库的通信通道
  • 打开连接
    使用DriverManager.getConnection()方法创建一个Connection对象,表示与数据库的物理连接
  • 执行查询
    使用类型为Statement的对象来构建和提交SQL语句到数据库
  • 从结果集中提取数据
    使用相应的ResultSet.getXXX()方法从结果集中检索数据
  • 释放资源
    关闭所有数据库资源,而不依赖于JVM的GC

建立连接

用到的核心组件

名称描述
DriverManager此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配
Driver此接口处理与数据库服务器的通信。通常很少会直接与Driver对象进行交互,而是使用DriverManager对象来管理这种类型的对象
Connection该界面具有用于联系数据库的所有方法,连接对象表示通信上下文

1、 导入JDBC包

import java.sql.*;

2、 注册JDBC驱动程序
使JVM将所需的驱动程序实现加载到内存中,以便满足JDBC请求

RDBMSJDBC驱动程序名称网址格式
MYSQL8com.mysql.cj.jdbc.Driverjdbc:mysql://hostname:3306/databaseName?serverTimezone=UTC
MySQLcom.mysql.jdbc.Driverjdbc:mysql://hostname:3306/databaseName
ORACLEoracle.jdbc.driver.OracleDriverjdbc:oracle:thin:@hostname:port Number:databaseName
DB2COM.ibm.db2.jdbc.DB2Driverjdbc:db2:hostname:port Number / databaseName
SYBASEcom.sybase.jdbc.SybDriverjdbc:sybase:Tds:hostname:port Number / databaseName
  • 方法一:Class.forName();
    注册驱动程序最常见的方法是使用Java的Class.forName()方法,将驱动程序的类文件动态加载到内存中,并将其自动注册
Class.forName("com.mysql.cj.jdbc.Driver");
  • 方法二:DriverManager.registerDriver();
    使用静态DriverManager.registerDriver()方法
Driver myDriver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver( myDriver );

3、 数据库URL配置

创建一个格式正确的地址,指向要连接到的数据库mydb010402

String url = "jdbc:mysql://localhost:3306/mydb010402?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";

链接地址一般有两种:

"jdbc:mysql://localhost:3306/数据库名?useSSL=false&useUnicode=true&characterEncoding=UTF-8"or"jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC"

4、 创建连接对象

加载驱动程序后,使用DriverManager.getConnection()方法建立数据库连接

  • 方法一
    getConnection(String url)
  • 方法二
    getConnection(String url,Properties prop)
  • 方法三
    getConnection(String url,String user,String password)
String userName = "root";
String passWord = "123456";
Connection connection = DriverManager.getConnection(url, userName, passWord);
  • 方法四
    使用数据库URL和属性对象
    DriverManager.getConnection(String url, Properties info);
import java.util.*;
String URL = "jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC";
Properties info = new Properties( );
info.put( "user", "username" );
info.put( "password", "password" );
Connection connection = DriverManager.getConnection(URL, info);

5、 关闭数据库
为确保连接关闭,可以在代码中提供一个“finally”块

if(connection != null){connection.close();
}

单表操作

用到的核心组件

名称描述
Statement使用从此接口创建的对象将SQL语句提交到数据库,除了执行存储过程之外一些派生接口还接受参数
ResultSet在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据,作为一个迭代器,允许我们移动其数据
SQLException此类处理数据库应用程序中发生的任何错误

Statement

执行方法描述
boolean execute(String SQL)检索到ResultSet对象返回true; 否则返回false【执行SQL DDL语句或需要使用真正的动态SQL时使用】
int executeUpdate(String SQL)返回受SQL语句执行影响的行数【使用此方法执行预期会影响多个行的SQL语句,例如INSERT,UPDATE或DELETE语句】
ResultSet executeQuery(String SQL)返回一个ResultSet对象【当希望获得结果集时,使用此方法(像使用SELECT语句一样)】

连接sql后,创建状态通道(进行sql语句的发送)

Statement statement = connection.createStatement();

查询操作

//执行查询
resultSet = statement.executeQuery("select * from employee");
//取出结果集信息
while (resultSet.next()) {//判断是否有下一条数据,类似于迭代器//取出数据:resultSet.getXXX("列名");XXX表示数据类型System.out.println("姓名:" + resultSet.getString("name") + ",生日:" + resultSet.getDate("birthday"));
}

连接sql及执行查询完整代码如下

class Demo {public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/mydb010402?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建状态通道(进行sql语句的发送)statement = connection.createStatement();//执行查询resultSet = statement.executeQuery("select * from employee");//3取出结果集信息while (resultSet.next()) {//判断是否有下一条数据,类似于迭代器//取出数据:resultSet.getXXX("列名");XXX表示数据类型System.out.println("姓名:" + resultSet.getString("name") + ",生日:" + resultSet.getDate("birthday"));}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {//5关闭资源try {if(resultSet != null){resultSet.close();}if(statement != null){statement.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finally}//end main
}//end class

增删改操作

//执行增删改查
int result = statement.executeUpdate("insert into employee(empid,name,birthday) values(2222,'二哈','2000-1-1')");
//返回结果为受影响的行数
if(result > 0){System.out.println("执行成功");
}else{System.out.println("执行失败");
}

连接sql及执行增删改完整代码如下

class Demo {public static void main(String[] args) {Connection connection = null;Statement statement = null;try {//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/mydb010402?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建状态通道(进行sql语句的发送)statement = connection.createStatement();//执行增删改查int result = statement.executeUpdate("insert into employee(empid,name,birthday) values(2222,'二哈','2000-1-1')");//返回结果为受影响的行数if(result > 0){System.out.println("执行成功");}else{System.out.println("执行失败");}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {//5关闭资源try {    if(statement != null){statement.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finally}//end main
}//end class

关闭Statement对象

为确保连接关闭,可以在代码中提供一个“finally”块

if(statement != null){statement.close();
}

ResultSet

ResultSet对象维护指向结果集中当前行的游标(结果集:包含在ResultSet对象中的行和列数据)

一般只需掌握以下两种即可:

类型描述
resultSet.getXXX(“列名”);取出数据,XXX表示数据类型
resultSet.next();判断是否有下一行数据

扩展:

类型描述
ResultSet.TYPE_SCROLL_INSENSITIVE光标可以向前和向后滚动,结果集对创建结果集后发生的数据库的其他更改不敏感
ResultSet.TYPE_SCROLL_SENSITIVE光标可以向前和向后滚动,结果集对创建结果集之后发生的其他数据库所做的更改敏感。
ResultSet.TYPE_FORWARD_ONLY光标只能在结果集中向前移动。

如果没有指定任何ResultSet类型,自动获得TYPE_FORWARD_ONLY


sql注入

简介

sql注入攻击是通过将恶意的Sql查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,是目前黑客对数据库进行攻击最常用手段之一

代码示例:
字符串username和password连接到sql后得到查询语句select * from users where username = “admin” and password= “abc” or 1=1
不管前面是否查询到数据,后面1=1恒为true,因此当通过此来查询用户名和密码进行登录时永远能登录成功

String username ="admin";
String password=" 'abc' or 1=1 ";
String sql="select * from users where username= '"+username+"' and password="+password;

此为使用Statement通道时存在的sql注入问题,避免sql注入可使用PreparedStatement(预状态通道)


避免sql注入

PreparedStatement接口扩展了Statement接口,提供了一个通用的Statement对象,动态地提供参数

StatementPreparedStatement
属于状态通道属于预状态通道
执行语句的时候进行编译先编译sql语句,再去执行,执行效率更高
不支持占位符支持占位符? ,给占位符赋值的时候,位置从1开始
存在sql注入问题可以防止sql注入(在处理值时以字符串的方式处理)

占位符:?
先占位,然后通过setXXX(index,vavle)方法对占位符进行赋值(XXX为数据类型,index从1开始)

代码示例:

			//定义sql,创建预状态通道(进行sql语句的发送)String sql = "select* from users where userName = ? and password = ? ";PreparedStatement pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始String uname = input.nextLine();String upass = input.nextLine();pps.setString(1,uname);pps.setString(2,upass);//执行sqlResultSet resultSet = pps.executeQuery();//返回结果为受影响的行数if(resultSet.next()){System.out.println("登录成功");}else{System.out.println("登录失败");}

关闭PreparedStatement对象

为确保连接关闭,可以在代码中提供一个“finally”块

if(pps != null){pps.close();
}

多表操作

多表操作首先要建立多表关系,再进行数据处理

两表操作有以下四种关系:
双向一对一
一对多
多对一
多对多

建立两表关系:
数据库通过外键建立两表关系
实体类通过属性的方式建立两表关系

实体类要求:
类名=表名
列名=属性名


以学生和老师为例,首先新建表student和teacher


根据表student和teacher在java中新建对应的类
类名=表名
列名=属性名

Student

public class Student {private int stuid;private String stuname;//外键列一般不生成属性public int getStuId() {return stuid;}public void setStuId(int stuid) {this.stuid = stuid;}public String getStuName() {return stuname;}public void setStuName(String stuname) {this.stuname = stuname;}
}

Teacher

public class Teacher {private int tid;private String tname;public int getTid() {return tid;}public void setTid(int tid) {this.tid = tid;}public String getTname() {return tname;}public void setTname(String tname) {this.tname = tname;}
}

一对多

一对多是在一方创建存储多方数据的集合,因此在teacher类中创建list存储多方(student)的数据,并添加set和get方法

    private List<Student> list = new ArrayList<Student>();//由于一位老师对应多位学生,因此学生用集合存储public List<Student> getList() {return list;}public void setList(List<Student> list) {this.list = list;}

一位老师对应多位学生,因此新建数据处理的接口TeacherDao,定义抽象操作方法

public interface TeacherDao {//定义操作方法//定义一个根据老师id查询老师信息(学生的信息)Teacher getById(int tid);
}

TeacherDaoImplements类实现TeacherDao接口,编写具体方法

public class TeacherDaoImplements implements TeacherDao {@Overridepublic Teacher getById(int tid) {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;Teacher teacher = new Teacher();try {//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建预状态通道(进行sql语句的发送)String sql = "select * from student s,teacher t where s.teacherid=t.tid and t.tid=?";pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始pps.setInt(1,tid);//执行sqlresultSet = pps.executeQuery();List<Student> students = new ArrayList<Student>();//老师→学生,存储学生信息,再add到老师while(resultSet.next()){//1.取出各自的信息teacher.setTid(resultSet.getInt("tid"));teacher.setTname(resultSet.getString("tname"));Student student = new Student();student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));//2.建立学生和老师之间的关系students.add(student);//每次将学生信息add到集合}teacher.setList(students);//set学生信息到teacher} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//5关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn teacher;//返回teacher}
}

最后编写操作实现类Test

public class Test {public static void main(String[] args) {TeacherDao dao = new TeacherDaoImplements();Teacher teacher = dao.getById(1);//查询id为1的老师及其学生System.out.println("teacher:" + teacher.getTname());//老师为一,因此只需输出一次List<Student> studentList = teacher.getList();//学生为多,因此集合存储,循环遍历输出for (Student student : studentList) {System.out.println("\t studentname=" + student.getStuName());}}
}

运行结果:

teacher:张三老师studentname=bbstudentname=ddstudentname=ee

sql查询结果:


多对一

与一对多不同,一对多是在一方创建存储多方数据的集合,多对一是在多方创建存储一方数据的对象

因此先在多方的学生类student中创建存储老师的对象,并添加set和get方法

    private Teacher teacher;public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}

多位学生对应一位老师,因此新建数据处理的接口Dao,定义抽象操作方法

public interface Dao {//定义操作方法//查询所有学生,学生中包含老师的信息public List<Student> getAll();
}

DaoImplements类实现Dao接口,编写具体方法

public class DaoImplements implements Dao {@Overridepublic List<Student> getAll() {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;List<Student> students = new ArrayList<>();//创建学生集合try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "select * from student s,teacher t where s.teacherid=t.tid";pps = connection.prepareStatement(sql);//执行sqlresultSet = pps.executeQuery();//取值while(resultSet.next()){Student student = new Student();student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));Teacher teacher = new Teacher();teacher.setTid(resultSet.getInt("tid"));teacher.setTname(resultSet.getString("tname"));//建立学生和老师之间的关系student.setTeacher(teacher);students.add(student);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn students;}//end method
}//end class

最后编写操作实现类Test

public class Test{public static void main(String[] args) {Dao dao = new DaoImplements();List<Student> students = dao.getAll();for (Student student : students) {System.out.println(student.getStuName()+","+student.getTeacher().getTname());}}
}

运行结果:

aaa,王五
bb,张三老师
cc,王五
dd,张三老师
ee,张三老师
ff,李四老师

sql查询结果:


一对一

以夫妻为例,首先新建表Husband和Wife

根据表Husband和Wife在java中新建对应的类
类名=表名
列名=属性名

  • Husband
public class Husband {private int husid;private String husname;public int getHusId() {return husid;}public void setHusId(int husid) {this.husid = husid;}public String getHusName() {return husname;}public void setHusName(String husname) {this.husname = husname;}
}
  • Wife
public class Wife {private int wifeid;private String wifeName;public int getWifeId() {return wifeid;}public void setWifeId(int wifeid) {this.wifeid = wifeid;}public String getWifeName() {return wifeName;}public void setWifeName(String wifeName) {this.wifeName = wifeName;}
}

一对一是在一方创建存储多方数据的集合,因此在husband和wife类中分别创建wife和husband,并添加set和get方法

  • husband中添加对应的wife信息
	private Wife wife;//一对一,一位丈夫对应一位妻子//获取和设置妻子的信息public Wife getWife() {return wife;}public void setWife(Wife wife) {this.wife = wife;}
  • wife中添加对应的husband信息
    private Husband husband;//一对一,一位妻子对应一位丈夫//获取和设置丈夫的信息public Husband getHusband() {return husband;}public void setHusband(Husband husband) {this.husband = husband;}

一位丈夫对应一位妻子,因此新建数据处理的接口Dao,定义抽象操作方法

public interface Dao {//查询妻子信息(包含丈夫信息)public Wife getWife(int wid);//查询丈夫信息(包含妻子信息)public Husband getHus(int hid);
}

DaoImplements类实现Dao接口,编写具体方法

  • 丈夫与妻子建立关系getWife
    @Overridepublic Wife getWife(int wid) {//连接sqlConnection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;List<Student> students = new ArrayList<>();//创建学生集合try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "SELECT* from wife w,husband h where w.hid = h.husid and w.wifeid = ?;";pps = connection.prepareStatement(sql);pps.setInt(1, wid);//执行sqlresultSet = pps.executeQuery();//取值Wife wife = new Wife();while (resultSet.next()) {//取出各自的信息wife.setWifeId(resultSet.getInt("wifeid"));wife.setWifeName(resultSet.getString("wifename"));Husband husband = new Husband();husband.setHusId(resultSet.getInt("husid"));husband.setHusName(resultSet.getString("husname"));//建立夫妻关系,丈夫与妻子建立关系wife.setHusband(husband);}return wife;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();} finally {try {//关闭资源if (resultSet != null) {resultSet.close();}if (pps != null) {pps.close();}if (connection != null) {connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}
  • 妻子与丈夫建立关系getHus
	@Overridepublic Husband getHus(int hid) {//连接sqlConnection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;List<Student> students = new ArrayList<>();//创建学生集合try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "SELECT* from wife w,husband h where w.hid = h.husid and h.husid = ?;";pps = connection.prepareStatement(sql);pps.setInt(1, hid);//执行sqlresultSet = pps.executeQuery();//取值Husband husband = new Husband();while (resultSet.next()) {//取出各自的信息husband.setHusId(resultSet.getInt("husid"));husband.setHusName(resultSet.getString("husname"));Wife wife = new Wife();wife.setWifeId(resultSet.getInt("wifeid"));wife.setWifeName(resultSet.getString("wifename"));//建立夫妻关系,妻子与丈夫建立关系husband.setWife(wife);}return husband;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();} finally {try {//5关闭资源if (resultSet != null) {resultSet.close();}if (pps != null) {pps.close();}if (connection != null) {connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}

最后编写操作实现类Test

public class Test{public static void main(String[] args) {DaoImplements dao = new DaoImplements();Wife wife = dao.getWife(1);System.out.println("wife:" + wife.getWifeName() + ", husband:" + wife.getHusband().getHusName());Husband hus = dao.getHus(2);System.out.println("husband:" + hus.getHusName() + ", wife:" + hus.getWife().getWifeName());}
}

运行结果:

wife:aa, husband:AA
husband:BB, wife:bb

sql查询结果:


多对多

多对多建表原则:
除了对应关系的两张表外,需要创建第三张表,该表至少两个字段,两个字段分别作为外键指向各自一方的主键

以课程和学生为例,多门课程对应一位学生,一位学生对应多门课程,因此课程与学生的关系为多对多

首先建立两张表subject和student


再建立subject和student的中间表middle,middle中除了自身的主键外,另外两个字段分别为subject和student的主键

根据表Subject和Student在java中新建对应的类
类名=表名
列名=属性名

  • Subject
public class Subject {private int subid;private String subname;public int getSubid() {return subid;}public void setSubid(int subid) {this.subid = subid;}public String getSubName() {return subname;}public void setSubName(String subname) {this.subname = subname;}
}
  • Student
public class Student {private int stuid;private String stuname;public int getStuId() {return stuid;}public void setStuId(int stuid) {this.stuid = stuid;}public String getStuName() {return stuname;}public void setStuName(String stuname) {this.stuname = stuname;}
}

多对多,即两方分别创建存储另一方数据的集合

因此先在多方的课程类subject和学生类student中分别创建存储对方的集合,并添加set和get方法

  • Subject中添加对应的学生信息
	private List stulist;//多对多,因此课程中存储student集合public List getStuList() {return stulist;}public void setStuList(List stulist) {this.stulist = stulist;}
  • Student中添加对应的课程信息
	private List<Subject> subjects;//多对多,因此学生中存储subject集合public List<Subject> getSubjects() {return subjects;}public void setSubjects(List<Subject> subjects) {this.subjects = subjects;}

多门课程对应多位学生,因此新建数据处理的接口Dao,定义抽象操作方法

public interface Dao {//查询某个学生信息(查询出所学科目)public Student findByStuId(int stuId);//查询某个科目以及对应的学生姓名public Subject findBySubId(int subId);
}

DaoImplements类实现Dao接口,编写具体方法

  • 学生 与课程建立关系findBySubId
	@Overridepublic Subject findBySubId(int subId) {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//定义sql,创建预状态通道(进行sql语句的发送)String sql = "select* from student st,subject su,middle m where st.stuid = m.stuid and su.subid = m.subid and su.subid = ?";pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始pps.setInt(1,subId);//执行sqlresultSet = pps.executeQuery();Subject subject = new Subject();List<Student> students = new ArrayList<>();while(resultSet.next()){//取出各自的信息subject.setSubid(resultSet.getInt("subid"));subject.setSubName(resultSet.getString("subname"));Student student = new Student();student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));students.add(student);}//建立student和subject之间的关系,subject→studentsubject.setStuList(students);//set学生信息到subjectreturn subject;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}
  • 课程 与学生建立关系findByStuId
	@Overridepublic Student findByStuId(int id) {Connection connection = null;PreparedStatement pps = null;ResultSet resultSet = null;try {//加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//获得链接String userName = "root";String passWord = "123456";String url = "jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";connection = DriverManager.getConnection(url, userName, passWord);//3定义sql,创建预状态通道(进行sql语句的发送)String sql = "select* from student st,subject su,middle m where st.stuid = m.stuid and su.subid = m.subid and st.stuid = ?";pps = connection.prepareStatement(sql);//给占位符赋值(index,value),下标从1开始pps.setInt(1,stuId);//执行sqlresultSet = pps.executeQuery();Student student = new Student();List<Subject> subjects = new ArrayList<>();while(resultSet.next()){//取出各自的信息student.setStuId(resultSet.getInt("stuid"));student.setStuName(resultSet.getString("stuname"));Subject subject = new Subject();subject.setSubid(resultSet.getInt("subid"));subject.setSubName(resultSet.getString("subname"));subjects.add(subject);}//建立subject和student之间的关系,student→subjectstudent.setSubjects(subjects);//set科目信息到studentreturn student;} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {//关闭资源if(resultSet != null){resultSet.close();}if(pps != null){pps.close();}if(connection != null){connection.close();}} catch (SQLException throwables) {throwables.printStackTrace();}}//end finallyreturn null;}

最后编写操作实现类Test

public class Test{public static void main(String[] args) {DaoImplements dao = new DaoImplements();//通过课程找学生Subject subject1 = dao.findBySubId(2);System.out.println("课程名称:" + subject1.getSubName());List<Student> studentList = subject1.getStuList();System.out.println("学习该课程的学生:");for (Student student1 : studentList) {System.out.println("\t"+student1.getStuName());}//通过学生找课程Student student2 = dao.findById(1);System.out.println("学生姓名:" + student2.getStuName());List<Subject> subjects = student2.getSubjects();System.out.println("学习的课程:");for (Subject subject2 : subjects) {System.out.println("\t"+subject2.getSubName());}}//end main
}//end class

运行结果:

课程名称:ui
学习该课程的学生:张三李四王五赵六花花潇潇
学生姓名:张三
学习的课程:javauih5c++

sql查询结果: