//Author 流云
//Version 1.0
一. 引言
1.1 如何操作数据库
使用客户端工具访问数据库,需要手工建立连接,输入用户名和密码登录,编写 SQL 语句,点击执行,查看操作结果(结果集或受影响行数)。
1.2 实际开发中,不会采用数据库客户端操作
在实际开发中,当用户的数据发生改变时,不可能通过客户端操作执行 SQL 语句,因为操作量过大,无法保证效率和正确性。
二. JDBC
2.1 介绍
JDBC(Java Database Connectivity) Java 连接数据库的规范(标准),可以使用 Java 语言连接数据库完成 CRUD 操作。
2.2 核心思想
Java 中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。由数据库厂商提供驱动实现类(Driver 数据库驱动)。
2.3 驱动包
mysql-connector-java-5.1.X 适用于 5.X 版本
mysql-connector-java-8.0.X 适用于 8.X版本
2.4 常用类
JDBC 是由多个接口和类进行功能实现。
2.5 环境搭建
在项目下新建 lib 文件夹,用于存放 jar 文件。
将 mysql 驱动mysql-connector-java-5.1.X复制到项目的 lib 文件夹中。
选中 lib 文件夹右键 Add as Libraay,点击 OK。
三. 开发步骤
3.1 注册驱动
使用 Class.forName("com.mysql.jdbc.Driver");手动加载字节码文件到 JVM 中。
Class.forName("com.mysql.jdbc.Driver");//加载驱动
3.2 连接数据库
通过 DriverManager.getConnection(url,user,password) 获取数据库连接对象。
URL:jdbc:mysql://localhost:3306/database
username:root
password:1234
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/database?
useUnicode=true&characterEncoding=utf8", "root","1234");
URL(Uniform Resource Locator) 统一资源定位符:由协议、IP、端口、SID(程序实例名称)组成。
3.3 获取执行对象
通过 Connection 对象获得 Statement 对象,用于对数据库进行通用访问。
Statement statement = conn.createStatement();
3.4 执行SQL 语句
执行 SQL 语句并接收执行结果。
String sql ="INSERT INTO t_jobs(JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES('JAVA_Le','JAVA_Lecturer',4000,10000);";
int result = statement.executeUpdate(sql);//执行SQL语句并接收结果
注意:在编写 DML 语句时,一定要注意字符串参数的符号是单引号 '值'。
DML 语句:增删改时,返回受影响行数(int 类型)。
DQL 语句:查询时,返回结果数据(ResultSet 结果集)。
3.5 处理结果
接受处理操作结果。
if(result == 1){
System.out.println("Success");
}
受影响行数:逻辑判断、方法返回。
查询结果集:迭代、依次获取。
3.6 释放资源
遵循先开后关原则,释放所使用到的资源对象。
statement.close();
conn.close();
3.7 综合案例
整合以上核心六步,实现向数据库表中插入一条数据。
package com.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class DeleteJdbc {
public static void main(String[] args) throws Exception{
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb","root","1234");
//3.获得执行SQL的对象
Statement statement = connection.createStatement();
//4.执行SQL语句,并接收结果
int result = statement.executeUpdate("delete from t_jobs where job_id = 'H5_mgr';");
//5.处理结果
if(result==1){
System.out.println("删除成功!");
}else{
System.out.println("删除失败!");
}
//6.释放资源
statement.close();
connection.close();
}
}
四. ResultSet
在执行查询 SQL 后,存放查询到的结果集数据。
4.1 接收结果集
ResultSet rs = statement.executeQuery(sql);
ResultSet rs= statement.executeQuery("SELECT * FROM t_employees;");
4.2 遍历结果集
ResultSet 以表(table)结构进行临时结果的存储,需要通过 JDBC API 将其中数据进行依次获取。
数据行指针:初始位置在第一行数据前,每调用一次 boolean next()方法ResultSet 的指针向下移动一行,结果为 true,表示当前行有数据。
rs.getXxx(整数);代表根据列的编号顺序获得,从 1 开始。
rs.getXxx("列名");代表根据列名获得。
boolean next() throws SQLException //判断 rs 结果集中下一行是否存在数据
4.2.1 遍历方法
int getInt(int columnIndex) throws SQLException //获得当前行第N列的int值
int getInt(String columnLabel) throws SQLException //获得当前行columnLabel列的int值
double getDouble(int columnIndex) throws SQLException //获得当前行第N列的double值
double getDouble(String columnLabel) throws SQLException //获得当前行columnLabel列的double值
String getString(int columnIndex) throws SQLException //获得当前行第N列的String值
String getString(String columnLabel) throws SQLException //获得当前行columnLabel列的String值
......
注意:列的编号从 1 开始。
4.3 综合案例
对 t_jobs 表中的所有数据进行遍历。
4.3.1 根据列名
package com.www.test;
import java.sql.*;
public class JobsQuery {
public static void main(String[] args) {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/product", "root", "1234");
//3.获取发送 sql 语句对象
Statement statement = connection.createStatement();
//4.执行 SQL 语句并接收结果集
ResultSet resultSet = statement.executeQuery("select * from t_jobs");
//5 处理结果集
while(resultSet.next()){
//5.1有数据,依据列名获取数据
String job_id = resultSet.getString("job_id");
String job_title = resultSet.getString("job_title");
int min_salary = resultSet.getInt("min_salary");
int max_salary = resultSet.getInt("max_salary");
System.out.println(job_id+"\t"+job_title+"\t"+min_salary+"\t"+max_salary);
}
//6.释放资源
rs.close();
statement.close();
connection.close();
}
}
4.3.2 根据编号
//。。。。与上无异
while(resultSet.next()){
//5.2有数据,依据列名获取数据
String job_id = resultSet.getString(1);
String job_title = resultSet.getString(2);
int min_salary = resultSet.getInt(3);
int max_salary = resultSet.getInt(4);
System.out.println(job_id+"\t"+job_title+"\t"+min_salary+"\t"+max_salary);
}
//释放资源
五. 常见错误
java.lang.ClassNotFoundException:找不到类(类名书写错误、没有导入jar包)。
java.sql.SQLException:与sql语句相关的错误 (约束错误、表名列名书写错误) 建议:在客户端工具中测试SQL语句之后再粘贴在代码中。
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 原因:列值Sting类型没有加单引号。
Duplicate entry '1' for key 'PRIMARY' 原因,主键值已存在或混乱,更改主键值或清空表。
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'password' in 原因:可能输入的值的类型不对,确定是否插入的元素时对应的值的类型正确。
六. 登录案例
6.1 创建表
创建一张用户表 User
id ,主键、自动增长。
用户名,字符串类型,唯一、非空
密码,字符串类型,非空
手机号码,字符串类型
插入 2 条测试语句
6.2 实现登录
通过控制台用户输入用户名和密码。
用户输入的用户名和密码作为条件,编写查询 SQL 语句。
如果该用户存在,提示登录成功,反之提示失败。
七. SQL注入
7.1 介绍
用户输入的数据中有 SQL 关键字或语法并且参与了 SQL 语句的编译,导致 SQL 语句编译后的条件含义为 true,一直得到正确的结果。这种现象称为 SQL 注入。
7.2 方案
由于编写的 SQL 语句是在用户输入数据,整合后再进行编译。所以为了避免 SQL 注入的问题,我们要使 SQL 语句在用户输入数据前就已进行编译成完整的 SQL 语句,再进行填充数据。
八. PreparedStatement
PreparedStatement 继承了 Statement 接口,执行 SQL 语句的方法无异。
8.1 应用
作用:
预编译SQL 语句,效率高。
安全,避免SQL注入 。
可以动态的填充数据,执行多个同构的 SQL 语句。
8.1.1 参数标记
//1.预编译 SQL 语句
PreparedStatement pstmt = conn.prepareStatement("select * from user where username=? and password=?");
注意:JDBC中的所有参数都由 ?符号占位,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。
8.1.2 动态参数绑定
pstmt.setXxx(下标,值) 参数下标从 1 开始,为指定参数下标绑定值。
//1.预编译 SQL 语句
PreparedStatement pstmt = conn.prepareStatement("select * from user where username=? and password=?");
//2.为参数下标赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
九. 封装工具类
在实际JDBC的使用中,存在着大量的重复代码:例如连接数据库、关闭数据库等这些操作!
我们需要把传统的JDBC代码进行重构,抽取出通用的JDBC工具类!以后连接任何数据库、释放资源都可以使用这个工具类。
9.1 重用性方案
封装获取连接、释放资源两个方法。
提供public static Connection getConnection(){}方法。
提供public static void closeAll(Connection conn , Statement sm , ResultSet rs){}方法。
9.1.1 实现
package com.jdbc;
import java.sql.*;
/**
* 重用性方案
* 获取连接
* 释放资源
*/
public class DBUtils {
static {//类加载,执行一次!
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//1.获取连接
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb", "root","1234");
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//2.释放资源
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
9.2 跨平台方案
定义public static final Properties prop = new Properties(); //读取配置文件的Map。
定义static{
//首次使用工具类时,加载驱动
InputStream is = JDBCUtil.class.getResourceAsStream("路径"); //通过复用本类自带流,读取jdbc.properties配置文件。classPath = bin
prop.load(is); //通过prop对象将流中的配置信息分割成键值对。
String driverName = prop.getProperty("driver"); //通过driverName的键获取对应的值(com.mysql.jdbc.Driver)
Class.forName(driverName); //加载驱动。
}
9.2.1 实现
在src 目录下新建 db.properties 文件。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb3
user=root
password=123456
工具类的封装。
package com.jdbc2;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class DBUtils {
private static final Properties PROPERTIES = new Properties();//存储配置文件的map
static {
InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
try {
PROPERTIES.load(is);//通过流,将配置文件内容加载到properties集合
Class.forName(PROPERTIES.getProperty("driver"));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(PROPERTIES.getProperty("url"),
PROPERTIES.getProperty("username"), PROPERTIES.getProperty("password"));
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
十. ORM
ORM(Object Relational Mapping)。
从数据库查询到的结果集(ResultSet)在进行遍历时,逐行遍历,取出的都是零散的数据。在实际应用开发中,我们需要将零散的数据进行封装整理。
10.1 实体类
一行数据中,多个零散的数据进行整理。
通过entity的规则对表中的数据进行对象的封装。
表名=类名;列名=属性名;提供各个属性的get、set方法。
提供无参构造方法、(视情况添加有参构造)。
10.1.1 ORM应用
entity实体类
package com.www.test;
public class T_Jobs {
private String job_id;
private String job_title;
private int min_salary;
private int max_salary;
@Override
public String toString() {
return "T_Jobs{" +
"job_id='" + job_id + '\'' +
", job_title='" + job_title + '\'' +
", min_salary=" + min_salary +
", max_salary=" + max_salary +
'}';
}
public String getJob_id() {
return job_id;
}
public void setJob_id(String job_id) {
this.job_id = job_id;
}
public String getJob_title() {
return job_title;
}
public void setJob_title(String job_title) {
this.job_title = job_title;
}
public int getMin_salary() {
return min_salary;
}
public void setMin_salary(int min_salary) {
this.min_salary = min_salary;
}
public int getMax_salary() {
return max_salary;
}
public void setMax_salary(int max_salary) {
this.max_salary = max_salary;
}
public T_Jobs() {
}
public T_Jobs(String job_id, String job_title, int min_salary, int max_salary) {
this.job_id = job_id;
this.job_title = job_title;
this.min_salary = min_salary;
this.max_salary = max_salary;
}
}
查询结果封装
package com.www.test;
import java.sql.*;
public class JobsQuery {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
String URL = "jdbc:mysql://localhost:3306/product";
String user = "root";
String password = "1234";
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
connection = DriverManager.getConnection(URL, user, password);
//3.编写 SQL 语句
String sql = "select * from t_jobs";
//4.获取发送 sql 语句对象
statement = connection.createStatement();
//5.执行 SQL 语句并接收结果集
resultSet = statement.executeQuery(sql);
//5.1使用 while 循环判断下一行是否有数据
while(resultSet.next()){
//5.2有数据,依据列名获取数据
String job_id = resultSet.getString(1);
String job_title = resultSet.getString(2);
int min_salary = resultSet.getInt(3);
int max_salary = resultSet.getInt(4);
//5.3 创建实体类对象
T_Jobs t_jobs = new T_Jobs();
//5.4 每列数据对应属性进行赋值
t_jobs.setJob_id(job_id);
t_jobs.setJob_title(job_title);
t_jobs.setMin_salary(min_salary);
t_jobs.setMax_salary(max_salary);
System.out.println(t_jobs);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
//6.释放资源
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
十一. 数据访问对象
DAO 实现了业务逻辑与数据库访问相分离。
对同一张表的所有操作封装在XxxDaoImpl对象中。
根据增删改查的不同功能实现具体的方法(insert、update、delete、select、 selectAll)。
11.1 创建数据库
创建一张表 Person,有以下列:
id:int,主键,自动增长
name:varchar(20) 非空
age:int 非空
bornDate:Date
email:字符串
address:字符串
11.2 封装实体类
创建entity实体类 Person,编写属性私有化,构造方法,get/set 方法。
11.3 编写 DaoImpl 类
编写 DaoImpl 类,提供增删改查方法,使用 JDBC 开发步骤,完成功能。
package com.person;
import sun.awt.image.DataBufferNative;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 增、删、改、查单个、查所有
* 只做数据库访问操作!不参与逻辑判断
* 数据库一张表的访问的操作复用!
*/
public class PersonDaoImpl {
//新增
public int insert(Person person){
Connection connection = null;
PreparedStatement preparedStatement = null;
String sql = "insert into person(name,age,borndate,email,address) values(?,?,?,?,?);";
try {
connection = DBUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,person.getName());
preparedStatement.setInt(2,person.getAge());
preparedStatement.setDate(3,null);
preparedStatement.setString(4,person.getEmail());
preparedStatement.setString(5,person.getAddress());
int result = preparedStatement.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,null);
}
return 0;
}
//修改
public int update(Person person){
Connection connection =null;
PreparedStatement preparedStatement = null;
String sql = "update person set
name=?,age=?,borndate=?,email=?,address=? where id = ?";
try {
connection = DBUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,person.getName());
preparedStatement.setInt(2,person.getAge());
preparedStatement.setDate(3,null);
preparedStatement.setString(4,person.getEmail());
preparedStatement.setString(5,person.getAddress());
preparedStatement.setInt(6,person.getId());
int result = preparedStatement.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,null);
}
return 0;
}
//删除
public int delete(int id){
Connection connection = null;
PreparedStatement preparedStatement = null;
String sql = "delete from person where id= ?;";
connection = DBUtils.getConnection();
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,id);
int result = preparedStatement.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,null);
}
return 0;
}
//查单个
public Person select(int id){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "select * from person where id = ?;";
Person person = null;
try {
connection = DBUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,id);
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
person = new Person();
int pid= resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
Date bornDate = resultSet.getDate("borndate");
String email = resultSet.getString("email");
String address = resultSet.getString("address");
person.setId(pid);
person.setName(name);
person.setAge(age);
person.setBornDate(bornDate);
person.setEmail(email);
person.setAddress(address);
}
return person;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,resultSet);
}
return null;
}
//查所有
public List<Person> selectAll(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
Person person = null;
List<Person> personList = new ArrayList<>();
try {
connection = DBUtils.getConnection();
preparedStatement = connection.prepareStatement("select * from person;");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
int pid= resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
Date bornDate = resultSet.getDate("borndate");
String email = resultSet.getString("email");
String address = resultSet.getString("address");
person = new Person(pid,name,age,bornDate,email,address);
personList.add(person);
}
return personList;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
十二. 三层架构
12.1 什么是三层
表示层:
命名:XXXView
职责:收集用户的数据和需求、展示数据。
业务逻辑层:
命名:XXXServiceImpl
职责:数据加工处理、调用DAO完成业务实现、控制事务。
数据访问层:
命名:XXXDaoImpl
职责:向业务层提供数据,将业务层加工后的数据同步到数据库。
12.2 三层搭建
utils 存放工具类(DBUtils)
entity 存放实体类(Person)
dao 存放 DAO 接口(PersonDao)
impl 存放 DAO 接口实现类(PersonDaoImpl)
service 存放 service 接口(PersonService)
impl 存放 service 接口实现类(PersonServiceImpl)
view 存放程序启动类(main)
程序设计时,考虑易修改、易扩展,为Service层和DAO层设计接口,便于未来更换实现类。
十三. 通用工具类
在DAO层中,对数据库表的增、删、改、查操作存在代码冗余,可对其进行抽取封装DaoUtils工具类实现复用。
13.1通用DML
/**
* 将所有数据表的操作,抽离成一个公用的方法;
* @Param sql 语句
* @Param sql语句对应的参数
* @return
*/
public static boolean executeDML(String sql,Object... params){
try {
//获取连接
Connection connection = getConnection();
//获取预处理的对象
PreparedStatement ps = connection.prepareStatement(sql);
//设置参数
if(null != params){
for(int i = 0 ; i < params.length ; i++){
//1:给占位符设置参数;2:获取参数
ps.setObject(i+1,params[i]);
}
}
int i = ps.executeUpdate();
close(null,ps,connection);
return i > 0;
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
return false;
}
13.2 通用DQL
/**
* 通用的查询;需要将我们的数据表信息,封装到List里面进行返回
* @param sql 查询语句
* @param cls 实体类的字节码对象
* @param params 查询语句里面的占位符
* @param <T> 实体类类型泛型
* @return 就是我们查询到的数据集合
*/
public static <T> List<T> executeDQL(String sql, Class<T> cls , Object...params) {
try {
Connection connection = getConnection(); //获取连接
//获取预处理sql
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//设置参数
if(null != params){
for(int i = 0 ; i < params.length ; i++){
preparedStatement.setObject(i+1,params[i]);
}
}
//执行查询 得到结果集
ResultSet resultSet = preparedStatement.executeQuery();
//获取元数据(我们的列名)
ResultSetMetaData metaData = resultSet.getMetaData();
//列的总数
int columnCount = metaData.getColumnCount();
//用来保存我们所有的表数据
List<T> list = new ArrayList<T>();
T t;//用来保存我们一条数据
//遍历结果集
while(resultSet.next()){ //每一行记录的遍历过程中
t = cls.newInstance();//根据字节码文件,构建一个实体类的对象
//获取所有列的数据
for(int i = 1 ; i <= columnCount ; i++){
//你的查询语句有时候有别名
String columnLabel = metaData.getColumnLabel(i);
//根据列名获取我们的记录
Object object = resultSet.getObject(columnLabel);
//需要将拿到的这一列的记录,放到t对象指定的属性里面;
//需要拿到这个t对象的属性,然后赋值;
//解决方案2: 如果Object是NULL没有必要设置了
if(null != object){
try {
Field field = cls.getDeclaredField(columnLabel);//根据属性的名称通过反射获取他的属性对象
field.setAccessible(true);
field.set(t,object);
} catch (NoSuchFieldException e) {
//代表我们当前这个实体类里面,没有这个属性
}
}
}
//需要将t对象保存到list
list.add(t);
}
close(resultSet,preparedStatement,connection);
return list;
} catch (SQLException sqlException) {
sqlException.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
13.3 Apache的DbUtils
13.3.1 简介
DbUtils是Java编程中数据库操作实用小工具,小巧、简单、实用。
对于数据表的查询操作,可以把结果转换为List、Array、Set等集合。便于操作。
对于数据表的DML操作,也变得很简单(只需要写SQL语句)。
13.3.2 API
ResultSetHandler接口:转换类型接口
BeanHandler类:实现类,把一条记录转换成对象
BeanListHandler类:实现类,把多条记录转换成List集合。
ScalarHandler类:实现类,适合获取一行一列的数据。
QueryRunner:执行sql语句的类
增、删、改:update();
查询:query();
13.3.3 使用步骤
导入jar包
mysql连接驱动jar包
druid-1.1.5.jar
database.properties配置文件
commons-dbutils-1.6.jar
13.3.4 代码
package com.project.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 连接池工具类
*/
public class DBUtils {
private static DruidDataSource dataSource;
static {
Properties properties = new Properties();
InputStream is = DBUtils.class.getResourceAsStream("/database.properties");
try {
properties.load(is);
dataSource = (DruidDataSource)
DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 返回一个数据源
public static DataSource getDataSource(){
return dataSource;
}
}
package com.project.dao.impl;
import com.project.dao.UserDao;
import com.project.entity.User;
import com.project.utils.DBUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
public class UserDaoImpl implements UserDao {
//1.创建QueryRunner对象,并传递一个数据源对象
private QueryRunner queryRunner = new QueryRunner(DBUtils.getDataSource());
@Override
public int insert(User user) {
Object[] params={user.getId(),user.getUsername(),user.getPassword(),user.getSex(),user.getEmail(),user.getAddress()};
try {
return queryRunner.update("insert into user(id,username,password,sex,email,address) values(?,?,?,?,?,?)",params);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
@Override
public int update(User user) {
Object[] params={user.getUsername(),user.getPassword(),user.getSex(),user.getEmail(),user.getAdd ress(),user.getId()};
try {
return queryRunner.update("update user set
username=?,password=?,sex=?,email=?,address=? where id = ?",params);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
@Override
public int delete(int id) {
try {
return queryRunner.update("delete from user where id = ?",id);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
@Override
public User select(int id) {
try {
//把查询到的记录封装成 指定对象
return queryRunner.query("select * from user where id = ?", new
BeanHandler<User>(User.class), id);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 查询所有
* @return
*/
@Override
public List<User> selectAll() {
try {
return queryRunner.query("select * from user;",new
BeanListHandler<User>(User.class));
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
十四. 连接池
在程序初始化时,预先创建指定数量的数据库连接对象存储在池中。当需要连接数据库时,从连接池中取出现有连接;使用完毕后,也不会进行关闭,而是放回池中,实现复用,节省资源。
资源重用。
更快的系统响应速度。
新的资源分配手段。
统一的连接管理,避免数据库连接泄漏。
14.1 Druid
14.1.1 步骤
创建 druid.properties 配置文件。
引入druid-xxx.jar 文件。
14.1.2 druid.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mytest
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=5000
14.1.3 连接池工具类
public class DbUtils {
//声明连接池对象
private static DruidDataSource ds;
static{
//实例化配置对象
Properties properties=new Properties();
try {
//加载配置文件内容
properties.load(DbUtils.class.getResourceAsStream("druid.properties"));
ds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//释放资源。。
}
14.2 HikariCP
引入HikariCP-xxx.jar 文件。
/*
一、主要配置
1、dataSourceClassName
这是DataSourceJDBC驱动程序提供的类的名称。请注意,如果您正在使用jdbcUrl“旧式”基于DriverManager的JDBC驱动程序配置,则不需要此属性 。 默认值:无
2、jdbcUrl
请注意,如果使用此属性,您仍然可以使用DataSource属性来配置您的驱动程序,实际上建议您使用URL本身中指定的驱动程序参数。 默认值:无
3、username
此属性设置从基础驱动程序获取连接时使用的默认身份验证用户名。
4、password
此属性设置从基础驱动程序获取连接时使用的默认身份验证密码。
*/
public static void main(String[] args) throws SQLException {
HikariConfig hikariConfig = new HikariConfig();
//设置url
hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/mytest?characterEncoding=UTF-8");
//数据库帐号
hikariConfig.setUsername("root");
//数据库密码
hikariConfig.setPassword("123456");
HikariDataSource ds = new HikariDataSource(hikariConfig);
}
十五 . 批处理
addBatch(); 添加到批处理
executeBatch(); 执行批处理
clearBatch(); 清空批处理
//如果保存1w条记录
String sql = "insert into userinfo(uname,upass) values(?,?)";
Connection connection = DBUtilPool.getConnectionByDruid();
long start = System.currentTimeMillis();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for(int i = 0 ; i < 10000 ; i++){
preparedStatement.setObject(1,"jack"+i);
preparedStatement.setObject(2,"aaaa"+i);
preparedStatement.addBatch(); //将当前的sql预编译好以后,加到批处理; 不会直接执行到数据库里面
if(i % 500 == 0){
preparedStatement.executeBatch(); //执行我们批处理
preparedStatement.clearBatch(); //把上面的执行了,需要清空
}
}
preparedStatement.executeBatch(); //最后一次进行批处理
long end = System.currentTimeMillis();
System.out.println("");
System.out.println(end-start);
十六. 事务
16.1 业务
代表用户完成的一个业务功能,可以由一个或多个DAO的调用组成。(软件所提供的一个功能都叫业务)。
16.2 service层
16.3 转账案例
BEGIN ;-- 代表我们开启了一个事务
UPDATE user_acount SET umoney = umoney-200 WHERE uname = 'jack';
-- 断网了 断电了
UPDATE user_acount SET umoney = umoney+200 WHERE uname = 'rose';
ROLLBACK -- 回滚事务(代表整个事务执行失败)
COMMIT; -- 提交事务
16.4 事务
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
16.5 转账事务案例
-- 默认提交事务
begin;
commit; -- 提交事务
rollback; -- 事务回滚
//代码里面默认回滚事务
connection.setAutoCommit(false); //不自动提交事务
connection.commit();//提交事务
connection.rollback();//回滚事务