
方式1: 使用第三方客户端来访问 MySQL:SQLyog

方式2: 使用命令行

我们今天要学习的是通过 Java程序 来访问 MySQL 数据库
Java 访问数据库的标准规范.是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范.
-- 创建 jdbc_user表
CREATE TABLE jdbc_user (
id INT PRIMARY KEY AUTO_INCREMENT ,
username VARCHAR(50),
PASSWORD VARCHAR(50),
birthday DATE
);
-- 添加数据
INSERT INTO jdbc_user (username, PASSWORD,birthday)
VALUES('admin1', '123','1991/12/24'),
('admin2','123','1995/12/24'),
('test1', '123','1998/12/24'),
('test2', '123','2000/12/24');
将MySQL驱动包添加到jar包库文件夹中,Myjar文件夹,用于存放当前项目需要的所有jar包

在 idea中 配置jar包库的位置




JDBC规范定义驱动接口:java.sql.Driver
MySql驱动包提供了实现类:com.mysql.jdbc.Driver
| 加载注册驱动的方式 | 描述 |
|---|---|
| Class.forName(数据库驱动实现类) | 加载和注册数据库驱动,数据库驱动由数据库厂商MySql提供 “com.mysql.jdbc.Driver” |
代码示例
public class JDBCDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.注册驱动(JDBC3 开始,可省略)
// forName 方法执行将类进行初始化
Class.forName("com.mysql.jdbc.Driver");
}
}
为什么这样可以注册驱动?
我们知道 Class类的forName方法 ,可以将一个类初始化, 现在我们一起Driver类的 看一下源码
// Driver类是MySql提供的数据库驱动类, 实现了JDBC的Driver接口 java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// 空参构造
public Driver() throws SQLException {
}
//静态代码块,Class类的 forName()方法将Driver类 加载到内存, static代码块会自动执行
static {
try {
/*
DriverManager 驱动管理类
registerDriver(new Driver) 注册驱动的方法
注册数据库驱动
*/
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
注:
从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。 Class.forName 这句话可以省略。
Connection 接口,代表一个连接对象 ,具体的实现类由数据库的厂商实现
使用 DriverManager类的静态方法,getConnection可以获取数据库的连接
| 获取连接的静态方法 | 说明 |
|---|---|
| Connection getConnection(String url, String user, String password) | 通过连接字符串和用户名,密码来获取数据 库连接对象 |
| 连接参数 | 说明 |
|---|---|
| user | 登录用户名 |
| password | 登录密码 |
| url | mySql URL的格式
|
对URL的详细说明
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。
第一部分是协议 jdbc,这是固定的;
第二部分是子协议,就是数据库名称,连接mysql数据库,第二部分当然是mysql了;
第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的IP地址(localhost)、端口号( 3306 ),以及要使用的 数据库名称 组成。

代码示例
public class JDBCDemo02 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接 url,用户名, 密码
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//com.mysql.jdbc.JDBC4Connection@2e3fc542
System.out.println(con);
}
}
通过Connection 的 createStatement方法 获取sql语句执行对象
| Connection接口中的方法 | 说明 |
|---|---|
| Statement createStatement() | 创建 SQL语句执行对象 |
Statement : 代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
| Statement类 常用方法 | 说明 |
|---|---|
int executeUpdate(String sql); | 执行insert update delete语句.返回int类型,代表受影响的行 数 |
ResultSet executeQuery(String sql); | 执行select语句, 返回ResultSet结果集对象 |
代码示例
public class JDBCDemo03 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接 url,用户名, 密码
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
Statement statement = con.createStatement();
//4.执行创建表操作
String sql = "create table test01(id int, name varchar(20),age int);";
//5.增删改操作 使用executeUpdate,增加一张表
int i = statement.executeUpdate(sql);
//6.返回值是受影响的函数
System.out.println(i);
//7.关闭流( 先开后关 )
statement.close();
con.close();
}
}
关闭流( 先开后关 )

只有在进行查询操作的时候, 才会处理结果集
代码示例
public class JDBCDemo04 {
public static void main(String[] args) throws SQLException {
//1.注册驱动 可以省略
//2.获取连接
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
Statement statement = con.createStatement();
String sql = "select * from jdbc_user";
//执行查询操作,返回的是一个 ResultSet 结果集对象
ResultSet resultSet = statement.executeQuery(sql);
//4.处理结果集 resultSet
}
}
| ResultSet接口方法 | 说明 |
|---|---|
| boolean next() | 1) 游标向下一行 2) 返回 boolean 类型,如果还有下一条记录,返回
|
| xxx getXxx( String or int) | 1) 通过列名,参数是 String 类型。返回对应的类型 2) 通过列号,参数是整数,从 1 开始。返回对应的类型 配合next() 使用(下一行) |

代码示例
public class JDBCDemo04 {
public static void main(String[] args) throws SQLException {
//1.注册驱动 可以省略
//2.获取连接
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
Statement statement = con.createStatement();
String sql = "select * from jdbc_user";
//执行查询操作,返回的是一个 ResultSet 结果对象
ResultSet resultSet = statement.executeQuery(sql);
//4.处理结果集
// //next 方法判断是否还有下一条数据
// boolean next = resultSet.next();
// System.out.println(next);
//
// //getXXX 方法获取数据 两种方式
// int id = resultSet.getInt("id");//列名
// System.out.println(id);
//
// int anInt = resultSet.getInt(1);//列号
// System.out.println(anInt);
//使用while循环
while(resultSet.next()){
//获取id
int id = resultSet.getInt("id");
//获取姓名
String username = resultSet.getString("username");
//获取生日
Date birthday = resultSet.getDate("birthday");
System.out.println(id + " : " +username + " : " + birthday);
}
//关闭连接
resultSet.close();
statement.close();
con.close();
}
}

需要释放的对象:ResultSet 结果集,Statement( 只有查询时 ) 语句,Connection 连接
释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection
放在哪个代码块中:finally 块
与IO流一样,使用后的东西都需要关闭!关闭的顺序是先开后关, 先得到的后关闭,后得到的先关闭


代码示例
public class JDBCDemo05 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1.注册驱动(省略)
//2.获取连接
String url = "jdbc:mysql://localhost:3306/db4";
connection = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
statement = connection.createStatement();
String sql = "select * from jdbc_user";
resultSet = statement.executeQuery(sql);
} catch (SQLException e) {
e.printStackTrace();
} finally {
/**
* 开启顺序: connection ==> statement => resultSet
* 关闭顺序: resultSet ==> statement ==> connection
*/
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
获取驱动(可以省略)
获取连接
获取Statement对象
处理结果集(只在查询时处理)
释放资源
什么时候自己创建工具类?
如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。
“获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。
工具类包含的内容
可以把几个字符串定义成常量:用户名,密码,URL,驱动类
得到数据库的连接:getConnection()
关闭所有打开的资源:
代码示例
/**
* JDBC 工具类
*/
public class JDBCUtils {
//1. 定义字符串常量, 记录获取连接所需要的信息
public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
public static final String USER = "root";
public static final String PASSWORD = "123456";
//2. 静态代码块, 随着类的加载而加载
static{
try {
//注册驱动
Class.forName(DRIVERNAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//3.获取连接的静态方法
public static Connection getConnection(){
try {
//获取连接对象
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//返回连接对象
return connection;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//关闭资源的方法
public static void close(Connection con, Statement st){
if(con != null && st != null){
try {
st.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement st, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
close(con,st);
}
}
解决插入中文乱码问题.
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
characterEncoding=UTF-8 指定字符的编码、解码格式。
代码示例
/**
* 插入数据
* @throws SQLException
*/
@Test
public void testInsert() throws SQLException {
//1.通过工具类获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取Statement
Statement statement = connection.createStatement();
//2.1 编写Sql
String sql = "insert into jdbc_user values(null,'张百万','123','2020/1/1')";
//2.2 执行Sql
int i = statement.executeUpdate(sql);
System.out.println(i);
//3.关闭流
JDBCUtils.close(connection,statement);
}
根据ID 需改用户名称
/**
* 修改 id 为1 的用户名为 广坤
*/
@Test
public void testUpdate() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
String sql = "update jdbc_user set username = '广坤' where id = 1";
statement.executeUpdate(sql);
JDBCUtils.close(connection,statement);
}
删除id为 3 和 4 的记录
/**
* 删除id 为 3 和 4的记录
* @throws SQLException
*/
@Test
public void testDelete() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
statement.executeUpdate("delete from jdbc_user where id in(3,4)");
JDBCUtils.close(connection,statement);
}
public class TestJDBC02 {
public static void main(String[] args) throws SQLException {
//1.获取连接对象
Connection connection = JDBCUtils.getConnection();
//2.获取Statement对象
Statement statement = connection.createStatement();
String sql = "SELECT * FROM jdbc_user WHERE username = '张百万';";
ResultSet resultSet = statement.executeQuery(sql);
//3.处理结果集
while(resultSet.next()){
//通过列名 获取字段信息
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String birthday = resultSet.getString("birthday");
System.out.println(id+" "+username+" " + password +" " + birthday);
}
//4.释放资源
JDBCUtils.close(connection,statement,resultSet);
}
}
向jdbc_user表中 插入两条数据
# 插入2条数据
INSERT INTO jdbc_user VALUES(NULL,'jack','123456','2020/2/24');
INSERT INTO jdbc_user VALUES(NULL,'tom','123456','2020/2/24');
SQL注入演示
# SQL注入演示
-- 填写一个错误的密码
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';

需求
步骤
得到用户从控制台上输入的用户名和密码来查询数据库
写一个登录的方法
a) 通过工具类得到连接
b) 创建语句对象,使用拼接字符串的方式生成 SQL 语句
c) 查询数据库,如果有记录则表示登录成功,否则登录失败
d) 释放资源
Sql注入方式: 123' or '1'=’1
public class TestLogin01 {
/**
* 用户登录案例
* 使用 Statement字符串拼接的方式完成查询
* @param args
*/
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取Statement
Statement statement = connection.createStatement();
//3.获取用户输入的用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名: ");
String name = sc.nextLine();
System.out.println("请输入密码: ");
String pass = sc.nextLine();
System.out.println(pass);
//4.拼接Sql,执行查询
String sql = "select * from jdbc_user " +
"where username = " + " '" + name +"' " +" and password = " +" '" + pass +"'";
System.out.println(sql);
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果集,判断结果集是否为空
if(resultSet.next()){
System.out.println("登录成功! 欢迎您: " + name);
}else {
System.out.println("登录失败!");
}
//释放资源
JDBCUtils.close(connection,statement,resultSet);
}
}



根据用户输入的数据,拼接处的字符串
select * from jdbc_user where username = 'abc' and password = 'abc' or '1'='1'
name='abc' and password='abc' 为假 '1'='1' 真
相当于 select * from user where true=true; 查询了所有记录

PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句对象.
预编译: 是指SQL 语句被预编译,并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
因为有预先编译的功能,提高 SQL 的执行效率。
可以有效的防止 SQL 注入的问题,安全性更高
| Connection 接口中的方法 | 说明 |
|---|---|
| PreparedStatement prepareStatement(String sql) | 指定预编译的 SQL 语句, SQL 语句中使用占位符 ? 创建一个语句对象 |
| 常用方法 | 说明 |
|---|---|
int executeUpdate(); | 执行insert update delete语句. |
ResultSet executeQuery(); | 执行select语句. 返回结果集对象 Resulet |
编写 SQL 语句,未知内容使用?占位:
"SELECT * FROM jdbc_user WHERE username=? AND password=?";
获得 PreparedStatement 对象
设置实际参数:setXxx( 占位符的位置, 真实的值)
执行参数化 SQL 语句
关闭资源
setXxx重载方法 (parameterIndex 参数位置) | 说明 |
|---|---|
| void setDouble(int parameterIndex, double x) | 将指定参数设置为给定 Java double 值。 |
| void setInt(int parameterIndex, int x) | 将指定参数设置为给定 Java int 值。 |
| void setString(int parameterIndex, String x) | 将指定参数设置为给定 Java String 值。 |
| void setObject(int parameterIndex, Object x) | 使用给定对象设置指定参数的值。 |

public class TestLogin02 {
/**
* 使用预编译对象 PrepareStatement 完成登录案例
* @param args
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取Statement
Statement statement = connection.createStatement();
//3.获取用户输入的用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名: ");
String name = sc.nextLine();
System.out.println("请输入密码: ");
String pass = sc.nextLine();
System.out.println(pass);
//4.获取 PrepareStatement 预编译对象
//4.1 编写SQL 使用 ? 占位符方式
String sql = "select * from jdbc_user where username = ? and password = ?";
PreparedStatement ps = connection.prepareStatement(sql);
//4.2 设置占位符参数
ps.setString(1,name);
ps.setString(2,pass);
//5. 执行查询 处理结果集
ResultSet resultSet = ps.executeQuery();
if(resultSet.next()){
System.out.println("登录成功! 欢迎您: " + name);
}else{
System.out.println("登录失败!");
}
//6.释放资源
JDBCUtils.close(connection,statement,resultSet);
}
}
public class TestPS {
public static void main(String[] args) throws SQLException {
Connection con = JDBCUtils.getConnection();
//获取 Sql语句执行对象
Statement st = con.createStatement();
//插入两条数据
st.executeUpdate("insert into jdbc_user values(null,'张三','123','1992/12/26')");
st.executeUpdate("insert into jdbc_user values(null,'李四','123','1992/12/26')");
//获取预处理对象
PreparedStatement ps = con.prepareStatement("insert into jdbc_user values(?,?,?,?)");
//第一条数 设置占位符对应的参数
ps.setString(1,null);
ps.setString(2,"长海");
ps.setString(3,"qwer");
ps.setString(4,"1990/1/10");
//执行插入
ps.executeUpdate();
//第二条数据
ps.setString(1,null);
ps.setString(2,"小斌");
ps.setString(3,"1122");
ps.setString(4,"1990/1/10");
//执行插入
ps.executeUpdate();
//释放资源
st.close();
ps.close();
con.close();
}
}

1. Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
2. PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
减少编译 次数提高数据库性能。-- 创建账户表
CREATE TABLE account(
-- 主键
id INT PRIMARY KEY AUTO_INCREMENT,
-- 姓名
NAME VARCHAR(10),
-- 转账金额
money DOUBLE
);
-- 添加两个用户
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000)
| 方法 | 说明 |
|---|---|
| void setAutoCommit(boolean autoCommit) | 参数是 true 或 false 如果设置为 false,表示关闭自动提交,相 当于开启事务 |
| void commit() | 提交事务 |
| void rollback() | 回滚事务 |
public class JDBCTransaction {
//JDBC 操作事务
public static void main(String[] args) {
Connection con = null;
PreparedStatement ps = null;
try {
//1. 获取连接
con = JDBCUtils.getConnection();
//2. 开启事务
con.setAutoCommit(false);
//3. 获取到 PreparedStatement 执行两次更新操作
//3.1 tom 账户 -500
ps = con.prepareStatement("update account set money = money - ? where name = ? ");
ps.setDouble(1,500.0);
ps.setString(2,"tom");
ps.executeUpdate();
//模拟tom转账后 出现异常
System.out.println(1 / 0);
//3.2 jack 账户 +500
ps = con.prepareStatement("update account set money = money + ? where name = ? ");
ps.setDouble(1,500.0);
ps.setString(2,"jack");
ps.executeUpdate();
//4. 正常情况下提交事务
con.commit();
System.out.println("转账成功!");
} catch (SQLException e) {
e.printStackTrace();
try {
//5. 出现异常回滚事务
con.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
//6. 最后关闭资源
JDBCUtils.close(con,ps);
}
}
}

普通 JDBC方式

连接池方式

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
常见的连接池有 DBCP连接池, C3P0连接池, Druid连接池, 接下里我们就详细学习一下
#创建数据库
CREATE DATABASE db5 CHARACTER SET utf8;
#使用数据库
USE db5;
#创建员工表
CREATE TABLE employee (
eid INT PRIMARY KEY AUTO_INCREMENT ,
ename VARCHAR (20), -- 员工姓名
age INT , -- 员工年龄
sex VARCHAR (6), -- 员工性别
salary DOUBLE , -- 薪水
empdate DATE -- 入职日期
);
#插入数据
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'李清照',22,'女',4000,'2018-11-12');
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'林黛玉',20,'女',5000,'2019-03-14');
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'杜甫',40,'男',6000,'2020-01-01');
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'李白',25,'男',3000,'2017-10-01');
tomcat内置的连接池。1 )将这两个 jar包添加到 myJar文件夹中 (jar包在资料里的软件文件夹中)

2 ) 添加myJar库 到项目的依赖中

连接数据库表的工具类, 采用DBCP连接池的方式来完成
Java中提供了一个连接池的规则接口 :DataSource , 它是java中提供的连接池
在DBCP包中提供了DataSource接口的实现类,我们要用的具体的连接池 BasicDataSource类
代码示例
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBCPUtils {
//1.定义常量 保存数据库连接的相关信息
public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8";
public static final String USERNAME = "root";
public static final String PASSWORD = "123456";
//2.创建连接池对象 (有DBCP提供的实现类)
public static BasicDataSource dataSource = new BasicDataSource();
//3.使用静态代码块进行配置
static{
dataSource.setDriverClassName(DRIVERNAME);
dataSource.setUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
}
//4.获取连接的方法
public static Connection getConnection() throws SQLException {
//从连接池中获取连接
Connection connection = dataSource.getConnection();
return connection;
}
//5.释放资源方法
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class TestDBCP {
/*
* 测试DBCP连接池
* */
public static void main(String[] args) throws SQLException {
//1.从DBCP连接池中拿到连接
Connection con = DBCPUtils.getConnection();
//2.获取Statement对象
Statement statement = con.createStatement();
//3.查询所有员工的姓名
String sql = "select ename from employee";
ResultSet resultSet = statement.executeQuery(sql);
//4.处理结果集
while(resultSet.next()){
String ename = resultSet.getString("ename");
System.out.println("员工姓名: " + ename);
}
//5.释放资源
DBCPUtils.close(con,statement,resultSet);
}
}

| 属性 | 描述 |
|---|---|
| driverClassName | 数据库驱动名称 |
| url | 数据库地址 |
| username | 用户名 |
| password | 密码 |
| maxActive | 最大连接数量 |
| maxIdle | 最大空闲连接 (假如有50个连接,10使用,40个空闲,剩下10个关闭) |
| minIdle | 最小空闲连接 ( 不够最小就会创建连接 ) |
| initialSize | 初始化连接 |

<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8property>
<property name="user">rootproperty>
<property name="password">123456property>
<property name="initialPoolSize">3property>
<property name="maxIdleTime">60property>
<property name="maxPoolSize">100property>
<property name="minPoolSize">10property>
default-config>
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db5property>
<property name="user">rootproperty>
<property name="password">123456property>
<property name="initialPoolSize">10property>
<property name="maxIdleTime">30property>
<property name="maxPoolSize">100property>
<property name="minPoolSize">10property>
named-config>
c3p0-config>
在项目下创建一个resource文件夹(专门存放资源文件)

选择文件夹,右键 将resource文件夹指定为资源文件夹

将文件放在resource目录下即可,创建连接池对象的时候会去加载这个配置文件

C3P0提供的核心工具类, ``ComboPooledDataSource , 如果想使用连接池,就必须创建该类的对象
xml中默认的配置xml中命名配置 public class C3P0Utils {
//1.创建连接池对象 C3P0对DataSource接口的实现类
//使用的配置是 配置文件中的默认配置
//public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
//使用指定的配置
public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
//获取连接的方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//释放资源
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class TestC3P0 {
//需求 查询姓名为杜甫的 记录
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection con = C3P0Utils.getConnection();
//2.获取预处理对象
String sql = "select * from employee where ename = ?";
PreparedStatement ps = con.prepareStatement(sql);
//3.设置占位符的值
ps.setString(1,"杜甫");
ResultSet resultSet = ps.executeQuery();
//4.处理结果集
while(resultSet.next()){
int eid = resultSet.getInt("eid");
String ename = resultSet.getString("ename");
int age = resultSet.getInt("age");
String sex = resultSet.getString("sex");
double salary = resultSet.getDouble("salary");
Date date = resultSet.getDate("empdate");
System.out.println(eid +" " + ename + " " + age +" " + sex +" " + salary +" "+date);
}
//5.释放资源
C3P0Utils.close(con,ps,resultSet);
}
}


Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。

是properties形式的
可以叫任意名称,可以放在任意目录下,我们统一放到 resources资源目录

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
获取数据库连接池对象
通过工厂来来获取 DruidDataSourceFactory类的createDataSource方法
createDataSource(Properties p) 方法参数可以是一个属性集对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(p);
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
//1.定义成员变量
public static DataSource dataSource;
//2.静态代码块
static{
try {
//3.创建属性集对象
Properties p = new Properties();
//4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//5. 使用Properties对象的 load方法 从字节流中读取配置信息
p.load(inputStream);
//6. 通过工厂类获取连接池对象
dataSource = DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接的方法
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//释放资源
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class TestDruid {
// 需求 查询 薪资在3000 到 5000之间的员工的姓名
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection con = DruidUtils.getConnection();
//2.获取Statement对象
Statement statement = con.createStatement();
//3.执行查询
ResultSet resultSet = statement.executeQuery("select ename from employee where salary between 3000 and 5000");
//4.处理结果集
while(resultSet.next()){
String ename = resultSet.getString("ename");
System.out.println(ename);
}
//5.释放资源
DruidUtils.close(con,statement,resultSet);
}
}



public class Employee implements Serializable {
private int eid;
private String ename;
private int age;
private String sex;
private double salary;
private Date empdate;
//空参 getter setter省略
}
DBUtils底层自动维护连接connection(自动模式 update不需要传入连接对象,也不需要关闭连接)手动模式
//手动方式 创建QueryRunner对象
QueryRunner qr = new QueryRunner();
自动模式
//自动创建 传入数据库连接池对象
QueryRunner qr2 = new QueryRunner(DruidUtils.getDataSource());
自动模式需要传入连接池对象, 在 DriuidUtils 工具类上添加一个方法
//自动创建 传入数据库连接池对象
//获取连接池对象
public static DataSource getDataSource(){
return dataSource;
}
| 参数 | 说明 |
|---|---|
| Connection conn | 数据库连接对象, 自动模式(构造方法)创建QueryRun 可以不传 ,手动模式(空参构造方法)必须传递 |
| String sql | 占位符形式的SQL ,使用 ? 号占位符 |
| Object… param | Object类型的 可变参,用来设置占位符上的参数 |
//手动方式 创建QueryRunner对象
QueryRunner qr = new QueryRunner();
@Test
public void testInsert() throws SQLException {
//1.创建 QueryRunner 手动模式创建
QueryRunner qr = new QueryRunner();
//2.编写 占位符方式 SQL
String sql = "insert into employee values(?,?,?,?,?,?)";
//3.设置占位符的参数
Object[] param = {null,"张百万",20,"女",10000,"1990-12-26"};
//4.执行 update方法
Connection con = DruidUtils.getConnection();
int i = qr.update(con, sql, param);
//5.释放资源
DbUtils.closeQuietly(con);
}
//修改操作 修改姓名为张百万的员工工资
@Test
public void testUpdate() throws SQLException {
//1.创建QueryRunner对象 自动模式,传入数据库连接池
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "update employee set salary = ? where ename = ?";
//3.设置占位符参数
Object[] param = {0,"张百万"};
//4.执行update, 自动模式 不需要传入连接对象,也不需要关闭连接
qr.update(sql,param);
}
//删除操作 删除id为1 的数据
@Test
public void testDelete() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "delete from employee where eid = ?";
//只有一个参数,不需要创建数组
qr.update(sql,1);
}
| ResultSetHandler 实现类 | 说明 |
|---|---|
| ArrayHandler | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这 条记录中的每一个字段的值 |
| ArrayListHandler | 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集 合中。 |
| BeanHandler | 将结果集中第一条记录封装到一个指定的javaBean中. new BeanHandler |
| BeanListHandler | 将结果集中每一条记录封装到指定的javaBean中,再将这些javaBean在封装到List 集合中 new BeanListHandler |
| ColumnListHandler | 将结果集中指定的列的字段值,封装到一个List集合中 |
| KeyedHandler | 将结果集中每一条记录封装到Map |
| MapHandler | 将结果集中第一条记录封装到了Map |
| MapListHandler | 将结果集中每一条记录封装到了Map |
| ScalarHandler | 它是用于封装单个数据。例如 select count(*) from 表操作。 |
QueryRunner的查询方法
query方法的返回值都是泛型,具体的返回值类型,会根据结果集的处理方式,发生变化
| 方法 | 说明 |
|---|---|
query(String sql, handler ,Object[] param) | 自动模式创建QueryRunner, 执行查询 |
| query(Connection con,String sql,handler,Object[] param) | 手动模式创建QueryRunner, 执行查询 |
创建一个测试类, 对ResultSetHandler接口的几个常见实现类进行测试
查询id为 5 的记录,封装到数组中
查询所有数据,封装到List集合中
查询id为 5 的记录,封装到指定JavaBean中
查询薪资大于 3000 的所员工信息,封装到JavaBean中再封装到List集合中
查询姓名是 张百万的员工信息,将结果封装到Map集合中
查询所有员工的薪资总额
查询id为 5 的记录,封装到数组中
/*
* 查询id为5的记录,封装到数组中
* ArrayHandler 将结果集的第一条数据封装到数组中
* */
@Test
public void testFindById() throws SQLException {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "select * from employee where eid = ?";
//3.执行查询
Object[] query = qr.query(sql, new ArrayHandler(), 5);
//4.获取数据
System.out.println(Arrays.toString(query));
}
查询所有数据,封装到List集合中
/**
* 查询所有数据,封装到List集合中
* ArrayListHandler可以将每条数据先封装到数组中, 再将数组封装到集合中
**/
@Test
public void testFindAll() throws SQLException {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "select * from employee";
//3.执行查询
List<Object[]> query = qr.query(sql, new ArrayListHandler());
//4.遍历集合获取数据
for (Object[] objects : query) {
System.out.println(Arrays.toString(objects));
}
}
根据ID查询,封装到指定JavaBean中
/**
* 查询id为3的记录,封装到指定JavaBean中
* BeanHandler 将结果集的第一条数据封装到 javaBean中
*
**/
@Test
public void testFindByIdJavaBean() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where eid = ?";
Employee employee = qr.query(sql, new BeanHandler<Employee>(Employee.class), 3);
System.out.println(employee);
}

查询薪资大于 3000 的所员工信息,封装到JavaBean中再封装到List集合中
/*
* 查询薪资大于 3000 的所员工信息,封装到JavaBean中再封装到List集合中
* BeanListHandler 将结果集的每一条和数据封装到 JavaBean中 再将JavaBean 放到list集合中
* */
@Test
public void testFindBySalary() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where salary > ?";
List<Employee> list = qr.query(sql, new BeanListHandler<Employee>(Employee.class), 3000);
for (Employee employee : list) {
System.out.println(employee);
}
}
查询姓名是 张百万的员工信息,将结果封装到Map集合中
/*
* 查询姓名是 张百万的员工信息,将结果封装到Map集合中
* MapHandler 将结果集的第一条记录封装到 Map中
* key对应的是 列名 value对应的是 列的值
* */
@Test
public void testFindByName() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where ename = ?";
Map<String, Object> map = qr.query(sql, new MapHandler(), "张百万");
Set<Map.Entry<String, Object>> entries = map.entrySet();
for (Map.Entry<String, Object> entry : entries) {
//打印结果
System.out.println(entry.getKey() +" = " +entry.getValue());
}
}
查询所有员工的薪资总额
/*
* 查询所有员工的薪资总额
* ScalarHandler 用于封装单个的数据
* */
@Test
public void testGetSum() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select sum(salary) from employee";
Double sum = (Double)qr.query(sql, new ScalarHandler<>());
System.out.println("员工薪资总额: " + sum);
}
Statement和PreparedStatement都支持批处理操作,这里我们介绍一下PreparedStatement的批处理方式:
要用到的方法
| 方法 | 说明 |
|---|---|
| void addBatch() | 将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中。 通过调用方法 executeBatch 可以批量执行此列表中的命令。 |
| int[] executeBatch() | 每次提交一批命令到数据库中执行,如果所有的命令都成功执行了, 那么返回一个数组,这个数组是说明每条命令所影响的行数 |

mysql 批处理是默认关闭的,所以需要加一个参数才打开mysql 数据库批处理,在url中添加
rewriteBatchedStatements=true
例如: url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&rewriteBatchedStatements=true
创建一张表
CREATE TABLE testBatch (
id INT PRIMARY KEY AUTO_INCREMENT,
uname VARCHAR(50)
)
测试向表中插入 1 万条数据
public class TestBatch {
//使用批处理,向表中添加 1万条数据
public static void main(String[] args) {
try {
//1.获取连接
Connection con = DruidUtils.getConnection();
//2.获取预处理对象
String sql ="insert into testBatch(uname) values(?)";
PreparedStatement ps = con.prepareStatement(sql);
//3.创建 for循环 来设置占位符参数
for (int i = 0; i < 10000 ; i++) {
ps.setString(1,"小强"+i);
//将SQL添加到批处理 列表
ps.addBatch();
}
//添加时间戳 测试执行效率
long start = System.currentTimeMillis();
//统一 批量执行
ps.executeBatch();
long end = System.currentTimeMillis();
System.out.println("插入10000条数据使用: " +(end - start) +" 毫秒!");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
除了表之外的数据都是元数据,可以分为三类
查询结果信息: UPDATE 或 DELETE语句 受影响的记录数。
数据库和数据表的信息: 包含了数据库及数据表的结构信息。
MySQL服务器信息: 包含了数据库服务器的当前状态,版本号等。
-- 元数据相关的命令介绍
-- 1.查看服务器当前状态
-- 2.查看MySQl的版本信息
-- 3.查询表中的详细信息
-- 4.显示数据表的详细索引信息
-- 5.列出所有数据库
-- 6.显示当前数据库的所有表
-- 7.获取当前的数据库名
show status; 查看服务器的状态信息

select version(); 获取mysql服务器的版本信息

show columns from table_name; 显示表的字段信息等,和desc table_name一样

show index from table_name; 显示数据表的详细索引信息,包括PRIMARY KEY(主键)

show databases:列出所有数据库
show tables : 显示当前数据库的所有表
select database(): 获取当前的数据库名
| 元数据类 | 作用 |
|---|---|
| DatabaseMetaData | 描述数据库的元数据对象 |
| ResultSetMetaData | 描述结果集的元数据对象 |
getMetaData ()connection 连接对象, 调用 getMetaData () 方法,获取的是DatabaseMetaData 数据库元数据对象
//2.获取代表数据库的 元数据对象 DatabaseMetaData
DatabaseMetaData metaData = connection.getMetaData();
PrepareStatement 预处理对象调用 getMetaData () , 获取的是ResultSetMetaData , 结果集元数据对象
//3.获取结果集元素据对象
ResultSetMetaData metaData = ps.getMetaData();
DatabaseMetaData的常用方法
| 方法说明 |
|---|
| getURL() : 获取数据库的URL |
| getUserName(): 获取当前数据库的用户名 |
| getDatabaseProductName(): 获取数据库的产品名称 |
| getDatabaseProductVersion(): 获取数据的版本号 |
| getDriverName(): 返回驱动程序的名称 |
| isReadOnly(): 判断数据库是否只允许只读 true 代表只读 |
ResultSetMetaData的常用方法
| 方法说明 |
|---|
| getColumnCount() : 当前结果集共有多少列 |
| getColumnName(int i) : 获取指定列号的列名, 参数是整数 从1开始 |
| getColumnTypeName(int i): 获取指定列号列的类型, 参数是整数 从1开始 |
public class TestMetaData {
//1.获取数据库相关的元数据信息 使用DatabaseMetaData
@Test
public void testDataBaseMetaData() throws SQLException {
//1.获取数据库连接对象 connection
Connection connection = DruidUtils.getConnection();
//2.获取代表数据库的 元数据对象 DatabaseMetaData
DatabaseMetaData metaData = connection.getMetaData();
//3.获取数据库相关的元数据信息
String url = metaData.getURL();
System.out.println("数据库URL: " + url);
String userName = metaData.getUserName();
System.out.println("当前用户: " + userName );
String productName = metaData.getDatabaseProductName();
System.out.println("数据库产品名: " + productName);
String version = metaData.getDatabaseProductVersion();
System.out.println("数据库版本: " + version);
String driverName = metaData.getDriverName();
System.out.println("驱动名称: " + driverName);
//判断当前数据库是否只允许只读
boolean b = metaData.isReadOnly(); //如果是 true 就表示 只读
if(b){
System.out.println("当前数据库只允许读操作!");
}else{
System.out.println("不是只读数据库");
}
connection.close();
}
//获取结果集中的元数据信息
@Test
public void testResultSetMetaData() throws SQLException {
//1.获取连接
Connection con = DruidUtils.getConnection();
//2.获取预处理对象
PreparedStatement ps = con.prepareStatement("select * from employee");
ResultSet resultSet = ps.executeQuery();
//3.获取结果集元素据对象
ResultSetMetaData metaData = ps.getMetaData();
//1.获取当前结果集 共有多少列
int count = metaData.getColumnCount();
System.out.println("当前结果集中共有: " + count + " 列");
//2.获结果集中 列的名称 和 类型
for (int i = 1; i <= count; i++) {
String columnName = metaData.getColumnName(i);
System.out.println("列名: "+ columnName);
String columnTypeName = metaData.getColumnTypeName(i);
System.out.println("类型: " +columnTypeName);
}
//释放资源
DruidUtils.close(con,ps,resultSet);
}
}

XML即可扩展标记语言(Extensible Markup Language)
特点
可扩展的, 标签都是自定义的
语法十分严格
XML能做什么?
| 功能 | 说明 |
|---|---|
| 存储数据 | 通常,我们在数据库中存储数据。不过,如果希望数据的可移植性更强,我们可以 把数据存储 XML 文件中 |
| 配置文件 | 作为各种技术框架的配置文件使用 (最多) |
| 在网络中 传输 | 客户端可以使用XML格式向服务器端发送数据,服务器接收到xml格式数据,进行解析 |
语法格式:
属性说明:
versioin :指定XML文档版本。必须属性,因为我们不会选择1.1,只会选择1.0;
encoding :指定当前文档的编码。可选属性,默认值是utf-8;
Element 元素: 是XML文档中最重要的组成部分
元素的命名规则
不能使用空格,不能使用冒号
xml 标签名称区分大小写
XML 必须有且只有一个根元素
语法格式:
<users><users>
XML 必须有且只有一个根元素,它是所有其他元素的父元素,比如以下实例中 users 就是根元素:
<users>
<user>
user>
<user>
user>
users>
普通元素的结构开始标签、元素体、结束标签组成。
<hello> 大家好 hello>
元素体:元素体可以是元素,也可以是文本
<hello>
<a>你好a>
hello>
空元素:空元素只有开始标签,而没有结束标签,但元素必须自己闭合
<close/>
<bean id="" class=""> bean>
属性是元素的一部分,它必须出现在元素的开始标签中
属性的定义格式:属性名=属性值,其中属性值必须使用单引或双引
一个元素可以有0~N个属性,但一个元素中不能出现同名属性
属性名不能使用空格、冒号等特殊字符,且必须以字母开头
XML的注释,注释内容会被XML解析器忽略!
以“ ”结束

<employees>
<employee eid="2">
<ename>林黛玉ename>
<age>20age>
<sex>女sex>
<salary>5000salary>
<empdate>2019-03-14empdate>
employee>
<employee eid="3">
<ename>杜甫ename>
<age>40age>
<sex>男sex>
<salary>15000salary>
<empdate>2010-01-01empdate>
employee>
employees>
在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
常见的xml约束:
DTD
Schema
作为程序员只要掌握两点
会阅读
会引入
不用自己编写

开发中,我们不会自己编写DTD约束文档
常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档。常见框架使用DTD约束有:
Struts2、hibernate等。
创建约束文件 student.dtd
<!ELEMENT students (student+) >
<!ELEMENT student (name,age,sex)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
<!ATTLIST student number ID #REQUIRED>
引入dtd文档到xml文档中,两种方式
内部dtd:将约束规则定义在xml文档中
外部dtd:将约束的规则定义在外部的dtd文件中
本地:
网络:
student.xml
DOCTYPE students SYSTEM "student.dtd">
<students>
<student number="s1">
<name>小斌name>
<age>22age>
<sex>男sex>
student>
<student number="s2">
<name>广坤name>
<age>55age>
<sex>男sex>
student>
students>
Schema是新的XML文档约束, 比DTD强大很多,是DTD 替代者;
Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。
Schema 功能更强大,内置多种简单和复杂的数据类型
Schema 支持命名空间 (一个XML中可以引入多个约束文档)
student.xsd
<xsd:schema xmlns="http://www.lagou.com/xml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lagou.com/xml"
elementFormDefault="qualified">
<xsd:element name="students" type="studentsType"/>
<xsd:complexType name="studentsType">
<xsd:sequence>
<xsd:element name="student" type="studentType" minOccurs="0"
maxOccurs="unbounded"/>
xsd:sequence>
xsd:complexType>
<xsd:complexType name="studentType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="age" type="ageType"/>
<xsd:element name="sex" type="sexType"/>
xsd:sequence>
<xsd:attribute name="number" type="numberType" use="required"/>
xsd:complexType>
<xsd:simpleType name="sexType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="male"/>
<xsd:enumeration value="female"/>
xsd:restriction>
xsd:simpleType>
<xsd:simpleType name="ageType">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="200"/>
xsd:restriction>
xsd:simpleType>
<xsd:simpleType name="numberType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="hehe_\d{4}"/>
xsd:restriction>
xsd:simpleType>
xsd:schema>
Xml Schema的根元素:

xml中引入schema约束的步骤:
查看schema文档,找到根元素,在xml中写出来
<students>
students>
根元素来自哪个命名空间。使用 xmlns 指令来声明
<students
xmlns="http://www.lagou.com/xml"
>
students>
引入 w3c的标准命名空间, 复制即可
<students
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
students>
引入的命名空间跟哪个xsd文件对应?
使用schemaLocation来指定:两个取值:第一个为命名空间 第二个为xsd文件的路径
<students
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lagou.com/xml student.xsd"
>
students>
命名空间
指的是一个环境,所用的标签来自于哪个环境定义的
student.xml
<students
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lagou.com/xml student.xsd"
>
<student number="hehe_1234">
<name>张百万name>
<age>25age>
<sex>femalesex>
student>
<student number="hehe_0000">
<name>小斌name>
<age>20age>
<sex>malesex>
student>
students>
开发中比较常见的解析方式有两种,如下:
DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。
优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
缺点:XML文档过大,可能出现内存溢出显现。
SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。(了解)
优点:占用内存少 处理速度快,可以处理大文件
缺点:只能读,逐行后将释放资源。

解析器:就是根据不同的解析方式提供的具体实现。有的解析器操作过于繁琐,为了方便开发人员,
有提供易于操作的解析开发包
JAXP:sun公司提供的解析器,支持DOM和SAX两种思想
DOM4J :一款非常优秀的解析器 , Dom4j是一个易用的、开源的库,用于XML,XPath和XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
Jsoup:jsoup 是一款Java 的HTML解析器 ,也可以解析XML
PULL:Android内置的XML解析方式,类似SAX。

使用核心类SaxReader加载xml文档获得Document,通过Document 对象获得文档的根元素,然后就可以操作了
常用API如下:
SaxReader对象
Document对象
Element对象
elements(…) 获得指定名称的所有子元素。可以不指定名称
element(…) 获得指定名称的第一个子元素。可以不指定名称
getName() 获得当前元素的元素名
attributeValue(…) 获得指定属性名的属性值
elementText(…) 获得指定名称子元素的文本值
getText() 获得当前元素的文本内容
编写user.xsd schema约束
<xsd:schema xmlns="http://www.lagou.com/xml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lagou.com/xml"
elementFormDefault="qualified">
<xsd:element name="users" type="usersType"/>
<xsd:complexType name="usersType">
<xsd:sequence>
<xsd:element name="user" type="userType" minOccurs="0"
maxOccurs="unbounded"/>
xsd:sequence>
xsd:complexType>
<xsd:complexType name="userType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="age" type="ageType" />
<xsd:element name="hobby" type="hobbyType" />
xsd:sequence>
<xsd:attribute name="id" type="numberType" use="required"/>
xsd:complexType>
<xsd:simpleType name="ageType">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="100"/>
xsd:restriction>
xsd:simpleType>
<xsd:simpleType name="hobbyType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="抽烟"/>
<xsd:enumeration value="喝酒"/>
<xsd:enumeration value="烫头"/>
xsd:restriction>
xsd:simpleType>
<xsd:simpleType name="numberType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d"/>
xsd:restriction>
xsd:simpleType>
xsd:schema>
编写user.xml 引入约束
<users
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lagou.com/xml user.xsd"
>
<user id="1">
<name>张百万name>
<age>20age>
<hobby>抽烟hobby>
user>
<user id="2">
<name>于谦name>
<age>50age>
<hobby>喝酒hobby>
user>
<user id="3">
<name>刘能name>
<age>40age>
<hobby>烫头hobby>
user>
users>
public class TestDOM4j {
//获取XML文件中的 所有的元素名称(标签)
@Test
public void test1() throws DocumentException {
//1.获取XML解析对象
SAXReader reader = new SAXReader();
//2.解析XML 获取 文档对象 document
Document document = reader.read("H:\\jdbc_work\\xml_task03\\src\\com\\lagou\\xml03\\user.xml");
//3.获取根元素
Element rootElement = document.getRootElement();
//获取根元素名称
System.out.println(rootElement.getName());
//获取 根元素下的标签
List<Element> elements = rootElement.elements();
for (Element element : elements) {
System.out.println("根标签下的子节点: " + element.getName());
List<Element> eList = element.elements();
for (Element e : eList) {
System.out.println("user标签下的子节点" + e.getName());
}
break;
}
}
/**
* 获取具体的节点内容 获取张百万的所有信息
*/
@Test
public void test2() throws DocumentException {
//1.创建XML文档解析对象
SAXReader sr = new SAXReader();
//2.读取XML获取到document对象
Document document = sr.read("src\\com\\lagou\\xml02\\user.xml");
//3.获取根节点
Element rootElement = document.getRootElement();
//4.得到当前节点的 所有子节点
List<Element> elements = rootElement.elements();
//5.获取第一个子节点
Element user = elements.get(0);
//6.获取所有信息
String id = user.attributeValue("id");
String name = user.elementText("name");
String age = user.elementText("age");
//使用getText获取当前元素的文本内容
String hobby = user.element("hobby").getText();
//打印
System.out.println(id+" " + name +" " + age +" " + hobby);
}
}

XPath 是一门在 XML 文档中查找信息的 语言 。 可以是使用xpath查找xml中的内容。
XPath 的好处

1 )需要再导入 jaxen-1.1-beta-6.jar

使用dom4j支持xpath的操作的几种主要形式
| 语法 | 说明 |
|---|---|
| /AAA/DDD/BBB | 表示一层一层的,AAA下面 DDD下面的BBB |
| //BBB | 表示和这个名称相同,表示只要名称是BBB,都得到 |
| //* | 所有元素 |
| BBB[1] , BBB[last()] | 第一种表示第一个BBB元素, 第二种表示最后一个BBB元素 |
| //BBB[@id] | 表示只要BBB元素上面有id属性,都得到 |
| //BBB[@id=‘b1’] | 表示元素名称是BBB,在BBB上面有id属性,并且id的属性值是b1 |
selectSingleNode(query): 查找和 XPath 查询匹配的一个节点。
selectNodes(query): 得到的是xml根节点下的所有满足 xpath 的所有节点;
Node: 节点对象
数据准备 book.xml
<bookstore>
<book id="book1">
<name>金瓶梅name>
<author>金圣叹author>
<price>99price>
book>
<book id="book2">
<name>红楼梦name>
<author>曹雪芹author>
<price>69price>
book>
<book id="book3">
<name>Java编程思想name>
<author>埃克尔author>
<price>59price>
book>
bookstore>
代码示例
使用selectSingleNode方法 查询指定节点中的内容
/*
* 1. 使用selectSingleNode方法 查询指定节点中的内容
* */
@Test
public void test1() throws DocumentException {
//1.创建解析器对象
SAXReader sr = new SAXReader();
//2.获取文档对象
Document document = sr.read("H:\\jdbc_work\\xml_task03\\src\\com\\lagou\\xml03\\book.xml");
//3.调用 selectSingleNode() 方法,获取name节点对象
Node node1 = document.selectSingleNode("/bookstore/book/name");
System.out.println("节点: " + node1.getName());
System.out.println("书名: " + node1.getText());
//4.获取第二本书的名称
Node node2 = document.selectSingleNode("/bookstore/book[2]/name");
System.out.println("第二本书的书名为: " + node2.getText());
}

使用selectSingleNode方法 获取属性值,或者属性值对应的节点
/*
* 2.使用selectSingleNode方法 获取属性值,或者属性值对应的节点
* */
@Test
public void test2() throws DocumentException {
//1.创建解析器对象
SAXReader sr = new SAXReader();
//2.获取文档对象
Document document = sr.read("H:\\jdbc_work\\xml_task03\\src\\com\\lagou\\xml03\\book.xml");
//3.获取第一个book节点的 id属性的值
Node node1 = document.selectSingleNode("/bookstore/book/attribute::id");
System.out.println("第一个book的id值为: " + node1.getText());
//4.获取最后一个book节点的 id属性的值
Node node2 = document.selectSingleNode("/bookstore/book[last()]/attribute::id");
System.out.println("最后一个book节点的id值为: " + node2.getText());
//5.获取id属性值为 book2的 书名
Node node3 = document.selectSingleNode("/bookstore/book[@id='book2']");
String name = node3.selectSingleNode("name").getText();
System.out.println("id为book2的书名是: " + name);
}
使用 selectNodes()方法 获取对应名称的所有节点
/
*
* 3.使用 selectNodes()方法 获取对应名称的所有节点
* *
*/
@Test
public void test3() throws DocumentException {
//1.创建解析器对象
SAXReader sr = new SAXReader();
//2.获取文档对象
Document document = sr.read("H:\\jdbc_work\\xml_task03\\src\\com\\lagou\\xml03\\book.xml");
//3.获取所有节点,打印节点名
List<Node> list = document.selectNodes("//*");
for (Node node : list) {
System.out.println("节点名: " + node.getName());
}
//4.获取所有的书名
List<Node> names = document.selectNodes("//name");
for (Node name : names) {
System.out.println(name.getText());
}
//5.获取指定 id值为book1的节点的所有 内容
List<Node> book1 = document.selectNodes("/bookstore/book[@id='book1']//*");
for (Node node : book1) {
System.out.println(node.getName()+" = " + node.getText());
}
}
创建自定义xml 文件, 保存 数据库连接信息
<jdbc>
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8property>
<property name="user">rootproperty>
<property name="password">123456property>
jdbc>
编写工具类 ,使用xpath 读取数据库信息
public class JDBCUtils {
//1. 定义字符串变量, 记录获取连接所需要的信息
public static String DRIVERNAME;
public static String URL;
public static String USER;
public static String PASSWORD;
//2.静态代码块
static {
try {
//使用 xpath读取 xml中的配置信息
SAXReader sr = new SAXReader();
Document document = sr.read("H:\\workspace01\\JDBC_day02\\src\\com\\lagou\\xml03\\jdbc-config.xml");
Node node = document.selectSingleNode("/jdbc/property[@name='driverClass']");
//System.out.println(node.getText());
DRIVERNAME = node.getText();
URL = document.selectSingleNode("/jdbc/property[@name='jdbcUrl']").getText();
USER = document.selectSingleNode("/jdbc/property[@name='user']").getText();
PASSWORD = document.selectSingleNode("/jdbc/property[@name='password']").getText();
//注册驱动
Class.forName(DRIVERNAME);
} catch (Exception e) {
e.printStackTrace();
}
}
//3.获取连接的静态方法
public static Connection getConnection(){
try {
//获取连接对象
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//返回连接对象
return connection;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
测试 : 获取所有员工的姓名
//获取所有员工的姓名
public static void main(String[] args) {
try {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.获取 statement ,执行SQL
Statement statement = connection.createStatement();
String sql = "select * from employee";
//3.处理结果集
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
String ename = resultSet.getString("ename");
System.out.println(ename);
}
} catch (SQLException e) {
e.printStackTrace();
}
}

通过对商城项目的部分表关系进行分析,完成数据库表的设计



创建名为 store的数据库, 对应商城项目
create database db6 character set utf8;
创建用户表
CREATE TABLE user (
uid varchar(32) PRIMARY KEY, -- 用户ID
username varchar(20) , -- 用户名
password varchar(20) , -- 密码
telephone varchar(20) , -- 电话
birthday date , -- 生日
sex varchar(10) -- 性别
);
INSERT INTO USER VALUES
('001','渣渣辉','123456','13511112222','2015-11-04','男'),
('002','药水哥','123456','13533334444','1990-02-01','男'),
('003','大明白','123456','13544445555','2015-11-03','男'),
('004','长海','123456','13566667777','2000-02-01','男'),
('005','乔杉','123456','13588889999','2000-02-01','男');
创建订单表
CREATE TABLE orders (
oid varchar(32) PRIMARY KEY, -- 订单id
ordertime datetime , -- 下单时间
total double , -- 总金额
name varchar(20), -- 收货人姓名
telephone varchar(20) , -- 电话
address varchar(30) , -- 地址
state int(11) , -- 订单状态
uid varchar(32), -- 外键字段 对应用户表id
CONSTRAINT ofk_0001 FOREIGN KEY (uid) REFERENCES user (uid)
);
-- 插入一条订单数据
INSERT INTO orders
VALUES('order001','2019-10-11',5500,'乔杉','15512342345','皇家洗浴',0,'001');
创建商品分类表
CREATE TABLE category (
cid varchar(32) PRIMARY KEY,
cname varchar(20)
);
INSERT INTO `category` VALUES ('1','手机数码'),('2','电脑办公'),('3','运动鞋服'),('4','图书音像');
创建商品表
CREATE TABLE product (
pid varchar(32) PRIMARY KEY, -- 商品id
pname varchar(50) , -- 商品名称
price double, -- 商品价格
pdesc varchar(255), -- 商品描述
pflag int(11) , -- 商品状态 1 上架 ,0 下架
cid varchar(32) , -- 外键对应 分类表id
KEY sfk_0001 (cid),
CONSTRAINT sfk_0001 FOREIGN KEY (cid) REFERENCES category (cid)
);
INSERT INTO `product` VALUES
('1','小米6',2200,'小米 移动联通电信4G手机 双卡双待',0,'1'),
('2','华为Mate9',2599,'华为 双卡双待 高清大屏',0,'1'),
('3','OPPO11',3000,'移动联通 双4G手机',0,'1'),
('4','华为荣耀',1499,'3GB内存标准版 黑色 移动4G手机',0,'1'),
('5','华硕台式电脑',5000,'爆款直降,满千减百',0,'2'),
('6','MacBook',6688,'128GB 闪存',0,'2'),
('7','ThinkPad',4199,'轻薄系列1)',0,'2'),
('8','联想小新',4499,'14英寸超薄笔记本电脑',0,'2'),
('9','李宁音速6',500,'实战篮球鞋',0,'3'),
('10','AJ11',3300,'乔丹实战系列',0,'3'),
('11','AJ1',5800,'精神小伙系列',0,'3');
订单项表 (中间表)
-- 订单项表
CREATE TABLE orderitem (
itemid VARCHAR(32) PRIMARY KEY, -- 订单项ID
pid VARCHAR(32), -- 外键 对应商品表 id
oid VARCHAR(32), -- 外键 对应订单表 id
KEY fk_0001 (pid),
KEY fk_0002 (oid),
CONSTRAINT fk_0001 FOREIGN KEY (pid) REFERENCES product (pid),
CONSTRAINT fk_0002 FOREIGN KEY (oid) REFERENCES orders (oid)
);
-- 向中间表中插入两条数据
INSERT INTO orderitem VALUES('item001','1','order001');
INSERT INTO orderitem VALUES('item002','11','order001');
com.lagou.app 测试包 用于对DAO代码进行测试
com.lagou.dao dao包 数据访问层,包含所有对数据库的相关操作的类
com.lagou.entity 实体包 保存根据数据库表 对应创建的JavaBean类
com.lagou.utils 工具包

我们只需要导入myjar仓库到项目中就可以了


driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db6?characterEncoding=UTF-8
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
package com.lagou.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
//1.定义成员变量
public static DataSource dataSource;
//2.静态代码块
static{
try {
//3.创建属性集对象
Properties p = new Properties();
//4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//5. 使用Properties对象的 load方法 从字节流中读取配置信息
p.load(inputStream);
//6. 通过工厂类获取连接池对象
dataSource = DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接的方法
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//获取Druid连接池对象的方法
public static DataSource getDataSource(){
return dataSource;
}
//释放资源
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.lagou.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtils {
/**
* 获取日期对象 格式化后的字符串
* @return
*/
public static String getDateFormart(){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sdf.format(date);
return format;
}
}
package com.lagou.utils;
import java.util.UUID;
/**
* UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
*
* UUID由以下几部分的组合:
* 1.当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
* 2.时钟序列。
* 3.全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
*
*/
public class UUIDUtils {
//获取唯一ID的 方法
public static String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
}
在Java一对多的数据关系中,需要遵循以下设计原则:
一个用户拥有多个订单,所以 用户是一的一方, 订单是多的一方

/**
* 用户表 对应 User类
* `uid` VARCHAR(32) NOT NULL,
* `username` VARCHAR(20) DEFAULT NULL,
* `password` VARCHAR(20) DEFAULT NULL,
* `telephone` VARCHAR(20) DEFAULT NULL,
* `birthday` DATE DEFAULT NULL,
* `sex` VARCHAR(10) DEFAULT NULL,
* */
public class User {
private String uid;
private String username;
private String password;
private String telephone;
private String birthday;
private String sex;
//提供 get set toString方法
}
/**
* 订单表
* `oid` VARCHAR(32) NOT NULL,
* `ordertime` DATETIME DEFAULT NULL,
* `total` DOUBLE DEFAULT NULL,
* `name` VARCHAR(20) DEFAULT NULL,
* `telephone` VARCHAR(20) DEFAULT NULL,
* `address` VARCHAR(30) DEFAULT NULL,
* `state` INT(11) DEFAULT NULL,
* `uid` VARCHAR(32) DEFAULT NULL,
*
* */
public class Orders {
private String oid; //订单号
private String ordertime; //下单时间
private double total; //订单的总金额
private String name; //收货人姓名
private String telephone; //收货人电话
private String address; //收货人地址
private int state; //订单状态 1 代表已支付 , 0 代表未支付
//订单属于哪个用户呢 ?
//提供 get set toString方法
}
第一种方式
根据两张表关系的描述 我们可以在 订单类中 添加一个uid 成员变量,表示订单属于哪个用户
private String uid;
但是这样设计会存在一些问题,比如 我要查询的是订单是属于哪个用户的用户名 ? 但是我们只有一个uid
第二种方式
Java类表示一对多关系,可以在多的一方添加一个成员变量,这个成员变量的类型 就是一的一方的类型.
再在订单表中 添加一个 User对象,User对象中 ,保存该订单关联的用户的所有信息
private String uid;
private User user; //保存该订单关联的 用户的所有信息
public class Orders {
private String oid; //订单号
private String ordertime; //下单时间
private double total; //订单的总金额
private String name; //收货人姓名
private String telephone; //收货人电话
private String address; //收货人地址
private int state; //订单状态 1 代表已支付 , 0 代表未支付
//订单属于哪个用户呢 ?
private String uid; //表示外键
private User user; //用来保存订单对应的详细的用户信息
//提供 get set toString方法
}
分类与商品 同样是一对多关系, 我们可以在多的一方进行操作 添加一个成员变量 类型是一的一方的类型

public class Category {
private String cid;
private String cname;
//提供 get set toString方法
}
public class Product {
private String pid;
private String pname;
private double price;
private String pdesc;
private int pflag; //是否上架 1 上架 ,0 下架
private String cid; //外键 对应分类表主键
private Category category; //用于保存Category的详细数据
//提供 get set toString方法
}
商品与订单是多对多关系, 一个订单上可以有多个商品, 一个商品可以出现在多个订单中.
多对多建表原则 需要一张中间表,中间表中至少有两个字段,作为中间表的外键分别指向另外两张表的主键

/**
* 订单项表(中间表)
* `itemid` VARCHAR(32) NOT NULL,
* `pid` VARCHAR(32) DEFAULT NULL,
* `oid` VARCHAR(32) DEFAULT NULL,
*
* */
public class OrderItem {
//订单项 指的是中间表中的一条数据
private String itemid; //订单项的id
private String pid; //外键 指向商品表主键
private String oid; //外键 指向订单表的主键
private Product product;//订单项内部的商品详细信息
private Orders orders;//订单项属于哪个订单
}
public class UserDao {
/**
* 注册用户
* */
public int register(User user) throws SQLException {
//1.获取QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "insert into user values(?,?,?,?,?,?)";
Object[] param = {user.getUid(), user.getUsername(), user.getPassword(),
user.getTelephone(), user.getBirthday(), user.getSex()};
//3.执行插入操作
int update = qr.update(sql,param);
//4.返回受影响的行数
return update;
}
/**
* 用户登录
* */
public User login(String username , String password) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from user where username = ? and password = ?";
//返回的是一个User对象 使用BeanHandler将结果集的第一条和数据封装到一个Javabean中
User user = qr.query(sql, new BeanHandler<User>(User.class), username, password);
return user;
}
}
public class TestUserDao {
//创建UserDao
UserDao userDao = new UserDao();
//测试注册功能
@Test
public void testRegister() throws SQLException {
//1. 创建User对象
User user = new User();
//2. 对User对象进行赋值
user.setUid(UUIDUtils.getUUID());
user.setUsername("大郎");
user.setPassword("654321");
user.setTelephone("15052005200");
user.setSex("男");
user.setBirthday(DateUtils.getDateFormart());
//3.执行注册
int register = userDao.register(user);
//4.判断注册是否成功
if(register > 0){
System.out.println("注册成功,欢迎您: " + user.getUsername());
}else{
System.out.println("注册失败! !");
}
}
//测试登录功能
@Test
public void testLogin() throws SQLException {
//调用UserDao的 login方法,传入用户名密码
User user = userDao.login("大郎", "654321");
//判断user不为空 登录成功
if(user != null){
System.out.println(user.getUsername() +" 欢迎您!");
}else{
System.out.println("用户名或者密码错误! !");
}
}
}
需求1: 根据商品ID 获取商品名称 ,商品价格 以及商品所属分类的名称
需求2: 根据分类ID 获取商品分类信息
需求3: 查询指定分类ID 下的商品个数
需求4: 查询指定分类ID 下的所有商品信息
public class ProductDao {
//1.根据商品ID 获取商品名称 ,商品价格 以及商品所属分类的名称
public Product findProductById(String pid) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from product where pid = ?";
Product product = qr.query(sql, new BeanHandler<Product>(Product.class), pid);
//调用 findCategoryById()方法, 传递外键cid 获取商品对应 的分类信息
Category category = findCategoryById(product.getCid());
//将category保存到商品对象中
product.setCategory(category);
return product;
}
//2.根据分类ID 获取商品分类信息
public Category findCategoryById(String cid) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from category where cid = ?";
Category category = qr.query(sql, new BeanHandler<Category>(Category.class),cid);
return category;
}
//3.查询指定分类ID 下的商品个数
public int getCount(String cid) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select count(*) from product where cid = ?";
//获取的单列数据 ,使用ScalarHandler 封装
Long count = (Long)qr.query(sql,new ScalarHandler<>(),cid);
//将Lang类型转换为 int 类型,并返回
return count.intValue();
}
//4.查询指定分类下的所有商品信息
public List<Product> findProductByCid(String cid) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from product where cid = ?";
//查询结果是一个List集合, 使用BeanListHandler 封装结果集
List<Product> list = qr.query(sql, new BeanListHandler<Product>(Product.class), cid);
return list;
}
}
public class TestProductDao {
ProductDao productDao = new ProductDao();
//1.测试 根据商品ID 获取商品名称 ,商品价格 以及商品所属分类的名称
@Test
public void testFindProductById() throws SQLException {
Product product = productDao.findProductById("1");
System.out.println("商品名称: "+product.getPname()+ ", 商品价格: " + product.getPrice() +
", 商品所属分类: "+ product.getCategory().getCname());
}
//2.测试 查询指定分类ID下的商品数
@Test
public void testGetCount() throws SQLException {
//查询 cid为3的分类下有多少个商品
int count = productDao.getCount("3");
System.out.println("分类ID为3的分类下商品个数: " + count);
}
//3.测试 查询指定分类下的所有商品信息
@Test
public void testFindProductByCid() throws SQLException {
//查询cid为 2的分类下 所有的商品信息
List<Product> list = productDao.findProductByCid("2");
for (Product product : list) {
System.out.println(product);
}
}
}
OrderItem表与Orders表的关系是 多对一

之前我们一直是在描述一对多,那么我们再反向描述一下 多对一
方式是在Orders中应该有一个 集合用来保存订单中的订单项信息

在Orders类中添加 订单项的集合
//该订单中有多少订单项
List<OrderItem> orderItems = new ArrayList<OrderItem>();
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
需求1: 获取 uid为 001 的用户的所有订单信息
需求2: 获取订单编号为 order001的订单中的所有商品信息
参数 oid, 返回值List 商品集合
-- 获取订单编号为: order001的订单中的所有商品信息
-- 1.查询订单项表中 oid是order001的 所有商品信息
SELECT
oi.pid
FROM orderitem oi WHERE oid = 'order001';
-- 2.将上面的查询语句作为in函数的条件, 查询product表
SELECT * FROM product WHERE pid IN
(SELECT
oi.pid
FROM orderitem oi WHERE oid = 'order001');
public class OrdersDao {
//需求1: 获取 uid为 001 的用户的所有订单信息
public List<Orders> findAllOrders(String uid) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from orders where uid = ?";
//一个用户所有的订单信息
List<Orders> ordersList = qr.query(sql, new BeanListHandler<Orders>(Orders.class), uid);
return ordersList;
}
//需求2: 获取订单编号为 order001的订单中的所有商品信息
public List<Product> findOrderById(String oid) throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//1.查询订单项表 获取订单项表中 订单ID为order001的数据
String sql = "SELECT pid FROM orderitem WHERE oid = ? ";
//2.查询的结果是 多条订单项数据
List<OrderItem> list = qr.query(sql, new BeanListHandler<OrderItem>(OrderItem.class), oid);
//3.创建集合保存商品信息
List<Product> productList = new ArrayList<>();
ProductDao productDao = new ProductDao();
//4.遍历订单项集合 获取Pid
for (OrderItem orderItem : list) {
//4.1从orderitem中获取 pid
String pid = orderItem.getPid();
//4.2 调用productDao
Product product = productDao.findProductById(pid);
//4.3 保存到集合
productList.add(product);
}
//返回 订单中对应的商品信息
return productList;
}
}
public class TestOrderDao {
OrdersDao ordersDao = new OrdersDao();
//1.获取 uid为 001 的用户的所有订单信息
@Test
public void testFindAllOrders() throws SQLException {
List<Orders> allOrders = ordersDao.findAllOrders("001");
//遍历打印订单信息
for (Orders order : allOrders) {
System.out.println(order);
}
}
//测试 获取订单编号为: order001的订单中的所有商品信息
@Test
public void testFindOrderById() throws SQLException {
List<Product> list = ordersDao.findOrderById("order001");
System.out.println("订单编号为order001中的商品有: ");
for (Product product : list) {
System.out.println(product);
}
}
}
创建数据库
create database test01 character set utf8;
创建表:account(账户表),有如下结构及数据:(复制以下SQL语句执行)
CREATE TABLE account (
id int(11) PRIMARY KEY AUTO_INCREMENT,
username varchar(100) , -- 用户姓名
card varchar(100) , -- 卡号
balance double -- 当前余额
) ;
-- 插入数据
insert into account(id,username,card,balance) values (1,'tom','1122334455',20000.00),(2,'lucy','55443332211',10000.00);
创建表:Transaction(交易记录表) (复制以下SQL语句执行)
CREATE TABLE TRANSACTION (
id INT PRIMARY KEY AUTO_INCREMENT,
cardid VARCHAR(100) , -- 交易卡号
tratype VARCHAR(100) , -- 交易类型: 转入 或者 转出
tramoney DOUBLE , -- 交易金额
tradate DATETIME -- 交易日期
) ;
创建项目 Test_01
导入jar包
创建包 包结构为
www.lagou.utils 存放工具类
www.lagou.entity 存放JavaBean类
www.lagou.app 创建测试类

package com.lagou.entity;
/**
* id int(11) PRIMARY KEY AUTO_INCREMENT,
* username varchar(100) , -- 用户姓名
* card varchar(100) , -- 卡号
* balance double, -- 当前余额
*
* */
public class Account {
private int id;
private String username;
private String card;
private double balance;
......
}
package com.lagou.entity;
/**
* id INT PRIMARY KEY AUTO_INCREMENT,
* cardid VARCHAR(100) , -- 交易卡号
* tratype VARCHAR(100) , -- 交易类型: 转入 或者 转出
* tramoney DOUBLE , -- 交易金额
* tradate DATETIME -- 交易日期
*
* */
public class Transaction {
private int id;
private String cardid;
private String tratype;
private double tramoney;
private String tradate;
......
}
按照步骤 实现卡号:1122334455向55443332211转账5000元的操作;
开启事务后,不要忘记提交事务package com.lagou.app;
import com.lagou.entity.Account;
import com.lagou.utils.DateUtils;
import com.lagou.utils.DruidUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
public class TestAccount {
// 实现卡号:1122334455向55443332211转账5000元的操作;
public static void main(String[] args) {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner();
//2.获取连接
Connection connection = DruidUtils.getConnection();
try {
//3.开启事务
connection.setAutoCommit(false);//设置为手动提交
//4.执行查询 获取卡号为1122334455的余额
String sql = "select balance from account where card = ? ";
Double balance = (Double) qr.query(connection,sql, new ScalarHandler<>(), "1122334455");
//5.判断余额是否不足
if(balance < 5000){
System.out.println("账户余额不足! 无法转账!");
return;
}
//4.进行转账操作
//账号 转出 1122334455 - 5000
String sql2 = "update account set balance = balance - ? where card = ? ";
qr.update(connection,sql2,5000,"1122334455");
//账号 转入 55443332211 + 5000
String sql3 = "update account set balance = balance + ? where card = ? ";
qr.update(connection,sql3,5000,"55443332211");
//5.处理交易记录
//转出记录
String sql4 = "insert into transaction values(?,?,?,?,?)";
Object[] param1 = {null,"1122334455","转出",5000, DateUtils.getDateFormart()};
qr.update(connection,sql4,param1);
//System.out.println(1/0);
//转入记录
String sql5 = "insert into transaction values(?,?,?,?,?)";
Object[] param2 = {null,"55443332211","转入",5000, DateUtils.getDateFormart()};
qr.update(connection,sql5,param2);
//所有操作执行完成提交事务
connection.commit();
System.out.println("转账成功 !!!");
} catch (Exception e) {
try {
//发生异常 立即回滚
connection.rollback();
System.out.println("发生异常! 转账失败!");
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
try {
//释放资源
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}



CREATE TABLE phone (
id INT PRIMARY KEY AUTO_INCREMENT,
pname VARCHAR(20),-- 手机名称
price DOUBLE , -- 手机单价
prodate DATE , -- 生产日期
color VARCHAR(20) -- 颜色
) ;
INSERT INTO phone(id,pname,price,prodate,color) VALUES
(1,'IPhone11',7800.00,'2019-07-20','土豪金'),
(2,'荣耀6X',5689.00,'2018-02-12','白色'),
(3,'诺基亚3',5699.00,'2011-12-05','银白色'),
(4,'红米6',599.00,'2017-01-18','香槟金');
继续在项目 Test_01中编写代码即可
package com.lagou.entity;
/**
* id INT PRIMARY KEY AUTO_INCREMENT,
* pname VARCHAR(20),-- 手机名称
* price DOUBLE , -- 手机单价
* prodate DATE , -- 生产日期
* color VARCHAR(20) -- 颜色
*
* */
public class Phone {
private int id;
private String pname;
private double price;
private String prodate;
private String color;
......
}
需求1: 查询价格高于2000元,生产日期是2019年之前的所有手机
需求2: 查询所有颜色是白色的手机信息
package com.lagou.app;
import com.lagou.entity.Phone;
import com.lagou.utils.DruidUtils;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
public class TestPhone {
//需求1: 查询价格高于2000元,生产日期是2019年之前的所有手机
@Test
public void test1() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from phone where price > ? and prodate < ?";
Object[] param = {2000,"2019-01-01"};
List<Phone> list = qr.query(sql, new BeanListHandler<Phone>(Phone.class), param);
System.out.println("符合条件的数据有: ");
for (Phone phone : list) {
System.out.println(phone);
}
}
//需求2: 查询所有颜色是白色的手机信息
@Test
public void test2() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from phone where color like ?";
List<Phone> list = qr.query(sql, new BeanListHandler<Phone>(Phone.class), "%白色%");
System.out.println("颜色是白色的手机: ");
for (Phone phone : list) {
System.out.println(phone);
}
}
}


创建表:dept(部门表),有如下结构及数据:(复制以下SQL语句执行)
CREATE TABLE dept (
id INT PRIMARY KEY AUTO_INCREMENT, -- 部门ID
deptname VARCHAR(20) DEFAULT NULL -- 部门名称
);
INSERT INTO dept(id,deptname) VALUES (1,'销售部'),(2,'财务部'),(3,'人事部');
创建表:employee(员工表) (复制以下SQL语句执行)
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT ,
NAME VARCHAR (30), -- 员工姓名
age DOUBLE , -- 员工年龄
sex VARCHAR (6), -- 员工性别
salary DOUBLE , -- 薪水
empdate DATE , -- 入职日期
did INT, -- 所属部门ID
FOREIGN KEY (did) REFERENCES dept (id)
);
INSERT INTO employee VALUES(1,'奥利给',35,'男',35000,'2000-10-07',1);
INSERT INTO employee VALUES(2,'小汉堡',32,'女',38000,'2005-11-12',2);
INSERT INTO employee VALUES(3,'哈拉少',30,'男',32000,'2008-03-14',2);
INSERT INTO employee VALUES(4,'广坤',40,'男',36000,'2007-09-18',NULL);
继续在项目 Test_01中编写代码即可
package com.lagou.entity;
/**
* id INT PRIMARY KEY AUTO_INCREMENT, -- 部门ID
* deptname VARCHAR(20) DEFAULT NULL -- 部门名称
*
* */
public class Dept {
private int id;
private String deptname;
......
}
package com.lagou.entity;
/**
* id INT PRIMARY KEY AUTO_INCREMENT ,
* NAME VARCHAR (30), -- 员工姓名
* age DOUBLE , -- 员工年龄
* sex VARCHAR (6), -- 员工性别
* salary DOUBLE , -- 薪水
* empdate DATE , -- 入职日期
* did INT, -- 所属部门ID
*
*/
public class Employee {
private int id;
private String name;
private double age;
private String sex;
private double salary;
private String empdate;
private int did; //外键
//表示一对多系 保存更加详细的部门信息
private Dept dept;
......
}
需求1: 查询所有的员工信息 (不包含没有部门的员工)。
需求2: 查询每个员工的 姓名, 薪资 和 所属部门名称
package com.lagou.app;
import com.lagou.entity.Dept;
import com.lagou.entity.Employee;
import com.lagou.utils.DruidUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
public class TestEmployee {
//1.需求1: 查询所有的员工信息 (不包含没有部门的员工)。
@Test
public void test1() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where did is not null";
List<Employee> list = qr.query(sql, new BeanListHandler<Employee>(Employee.class));
for (Employee employee : list) {
System.out.println(employee);
}
}
//2.需求2: 查询每个员工的 姓名, 薪资 和 所属部门名称
@Test
public void test2() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where did is not null";
List<Employee> list = qr.query(sql, new BeanListHandler<Employee>(Employee.class));
String sql2 ="select * from dept where id = ?";
//根据 did进行查询, 将查询到的部门信息分组到对应的 员工对象中
for (Employee employee : list) {
Dept dept = qr.query(sql2, new BeanHandler<Dept>(Dept.class), employee.getDid());
employee.setDept(dept);
}
//打印
for (Employee employee : list) {
System.out.println("员工姓名: "+employee.getName()+ ",员工薪资: " +employee.getSalary() +
",所属部门: " + employee.getDept().getDeptname());
}
}
}

