引言:
本文主要分享了有关JDBC的内容包括:JDBC的概述(思想和环境的搭建)、JDBC的核心6步、ResultSet结果集、SQL注入问题、Statement接口、PreparedStatement接口(案例)、登录案例、封装工具类、ORM、DAO、日期类型的封装、JDBC事务、连接池(Druid德鲁伊连接池)以及若干案例;
JDBC(Java DataBase Connectivity):Java连接数据库,可以使用Java语言连接数据库完成CRUD操作;
Java中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式;
由数据库厂商提供驱动实现类(Driver数据库驱动);
JDBC下载路径:https://dev.mysql.com/downloads/connector/
JDBC 是由多个接口和类进行功能实现;
使用Class.forName(“com.mysql.jdbc.Driver”);手动加载字节码文件到JVM中
Class.forName("com.mysql.jdbc.Driver");
通过DriverManager.getConnection(url,user,password);获得数据库连接对象
Connection connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf8","root","root");
通过Connection对象获得Statement对象,用于对数据库进行通用访问的
功能:针对预计执行更新操作
- Statement createStatement() :创建一条 SQL 语句对象
Statement statement = connection.createStatement();
编写SQL语句,并执行,接收执行后的结果
int executeUpdate(String sql:静态sql) :DDL(不返回内容) ,DML(操作语句,返回具体的行数)
- 参数:SQL 语句
- 返回值:返回对数据库影响的行数
ResultSet executeQuery(String sql):用于发送 DQL 语句,执行查询的操作。select
- 参数:SQL 语句
- 返回值:查询的结果集
String sql = "update stu set student_name = '张三' where student_id = '1004'";
int result = statement.executeUpdate(sql);
接收并处理操作结果
if(result > 0){
System.out.println("成功!");
}else{
System.out.println("失败!");
}
遵循的是先开后关的原则,释放过程中用到的所有资源对象
statement.close();
connection.close();
综合核心六步,实现增删改
public static void main(String[] args) throws Exception {
//1.加载驱动 将驱动字节码文件加载到JVM中
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库
String url = "jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8";//连接地址
String user = "root" ;
String password = "root";
Connection connection = DriverManager.getConnection(url,user,password);
//测试是否连接成功
System.out.println(connection);
//3.获取执行SQL的对象 Statement
Statement statement = connection.createStatement();
//4.编写SQL语句,并执行SQL语句
//增加
String sql1 = "INSERT INTO stu(student_id,student_name,sex,birthday,phone,GradeId) VALUES('1003','kaka','男','2001-06-01','123456189',1);";
//删除
String sql2 = "delete from stu where student_id = '1003'";
//修改
String sql3 = "update stu set student_name = '张三' where student_id = '1004'";
int result = statement.executeUpdate(sql3);
//5.处理结果
if(result > 0){
System.out.println("成功!");
}else{
System.out.println("失败!");
}
//6.释放资源 先开后关
statement.close();
connection.close();
}
}
在执行查询SQL后,存放查询到的结果集数据;
ResultSet resultSet = statement.executeQuery(sql);
ResultSet executeQuery(String sql):用于发送 DQL 语句,执行查询的操作,select
- 参数:SQL 语句
- 返回值:查询的结果集
String sql = "select student_id,student_name,sex,birthday,phone,GradeId from stu";
ResultSet resultSet = statement.executeQuery(sql);
ResultSet以表(Table)结构进行临时结果的存储,需要通过JDBC API将其中的数据进行依次获取
- boolean next():数据行指针,初始位置在第一行数据前,每调用一次boolean next()方法,ResultSet中指针向下移动一行,结果为true,表示当前行有数据;
- int /String …getInt(int columnIndex) :获取当前列的编号对应的内容第一列就是1…
- int/String…getInt(String Stringcolumn):通过类的名称获取的信息
boolean next() throws SQLException;//判断resultSet结果集中下一行是否有数据
int getInt(int columnIndex) throws SQLException;//获得当前行的第N列的int值
int getInt(String columnLabel) throws SQLException;//获得当前行columnLabel列的int值
/**
* 方式一
* 根据列名
* JDBC查询数据库表的数据
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestDQL {
public static void main(String[] args) throws Exception{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接
String url = "jdbc:mysql://localhost:3306/companydb?useUnicode = true&characterEncoding = utf8";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url,user,password);
//3.获取执行的对象
Statement statement = connection.createStatement();
//4.编写SQL语句 查询
String sql = "select student_id,student_name,sex,birthday,phone,GradeId from stu";
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果
while(resultSet.next()){//判断是否有下一行
//1.根据列名获取当前行的每一列数据
String student_id = resultSet.getString("student_id");
String student_name = resultSet.getString("student_name");
String sex = resultSet.getString("sex");
String birthday = resultSet.getString("birthday");
String phone = resultSet.getString("phone");
String gradeId = resultSet.getString("gradeId");
System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
}
//6.释放资源
resultSet.close();
statement.close();
connection.close();
}
}
//方式二前面步骤如上:
//5.处理结果
while(resultSet.next()){//判断是否有下一行
//1.根据列名获取当前行的每一列数据
String student_id = resultSet.getString(1);
String student_name = resultSet.getString(2);
String sex = resultSet.getString(3);
String birthday = resultSet.getString(4);
String phone = resultSet.getString(5);
String gradeId = resultSet.getString(6);
System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
}
- java.lang.ClassNotFoundException :找不到类(类名书写错误、没有导入jar包);
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException :与SQL语句相关的错误(表名列名书写错误、约束错误、插入的值是String类型,但是没有加单引号)建议:在客户端工具中测试sql语句后,再粘贴到代码中来;
- com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry ‘1003’ for key ‘PRIMARY’ :主键值已存在,更改要插入的主键值;
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Unknown column ‘password’ in :可能输入的值的类型不对,确定插入元素时,对应的值的类型是否正确;
当用户输入的数据中有SQL关键字或语法时,并且参与了SQL语句的编译,导致SQL语句编译后条件结果为true,一直得到正确的结果。称为SQL注入;
避免SQL注入:
由于编写的SQL语句,是在用户输入数据后,整合后再编译成SQL语句。所以为了避免SQL注入的问题,使SQL语句在用户输入数据前,SQL语句已经完成编译,成为了完整的SQL语句,再进行填充数据;
PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法,是一个预编译的 SQL 语句;
好处:
- prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率;
- 安全,避免SQL注入;
- 可以动态的填充数据,执行多个同构的SQL语句;
- 提高可读性;
- PreparedStatement prepareStatement(String sql):指定预编译的 SQL 语句,SQL 语句中使用占位符? 创建一个语句对象;
- int executeUpdate() :执行 DML,增删改的操作,返回影响的行数;
- ResultSet executeQuery():执行 DQL,查询的操作,返回结果集;
ResultSet executeQuery():操作DQL语句进行查询
//1.预编译SQL语句
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setXxx(下标,值):参数下标是从1开始,为指定占位符下标绑定值;
//2.为占位符下标赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
- 编写 SQL 语句,未知内容使用?占位:“SELECT * FROM user WHERE name=? AND password=?”;
- )获得 PreparedStatement 对象;
- 设置实际参数:setXxx(占位符的位置, 真实的值);
- 执行参数化 SQL 语句;
- 关闭资源;
public User select(int id){
connection = DBUtils.getConnection();
String sql = "select id,username,password,sex,email,address from user where id = ?;";
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,id);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String sex = resultSet.getString("sex");
String email = resultSet.getString("email");
String address = resultSet.getString("address");
User user = new User(id,username,password,sex,email,address);
return user;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,resultSet);
}
return null;
}
Statement:
- 执行对象执行对应的sql语句(DDl,DQL,DML)
- DDL/DML:executeUpdate(String sql) — >int
- DQL:executeQuery(String sql) ---->ResultSet 结果集
- 安全隐患:
- 在拼接sql语句,使用一些关键字和表中的字段进行拼接(SQL注入)
- 执行效率低
- 录入不存在用户名以及不存在密码依然可以登录成功
PreparedStatement:
- 获取预编译对象,会将参数化的sql发送给数据库
- 通过setXXX(参数1,参数2)给这些占位符进行赋值
- XXX:表示类型 :给id字段设置数据如:setInt(1,1)
- 参数1:当前预编译的sql语句中的第几个占位符
- 参数2:给当前这个占位符赋值的实际值是什么
- 预编译对象进行参数赋值
- 预编译对象.setString(1,“zhangsan”)
- 预编译对象.setString(2,“123”) ;
- 特点:
- 防止的SQL注入(更安全的)
- 执行效率高
CREATE TABLE `user`(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20) NOT NULL,
`password` VARCHAR(20) NOT NULL,
phone VARCHAR(11)
)CHARSET = utf8;
INSERT INTO `user`(username, `password`,phone) VALUES ('kaka','123','12345678910');
INSERT INTO `user`(username, `password`,phone) VALUES ('tangtang','456',NULL);
SELECT * FROM `user`;
/**
* 登录操作
* 通过控制台,用户输入用户名和密码;
* 用户输入的用户名和密码作为参数,编写查询SQL语句;
* 如果查询到用户,则用户存在,提示登录成功,反之,提示失败;
*/
import java.util.Scanner;
import java.sql.*;
public class TestSafeLogin {
public static void main(String[] args) throws Exception{
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接
String url = "jdbc:mysql://localhost:3306/companydb?useUnicode = true&characterEncoding = utf8";
Connection connection = DriverManager.getConnection(url,"root","root");
//3.获取执行的对象
String sql ="select * from user where username = ? and password = ?";
//预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//为SQL语句赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
//4.执行SQL语句
ResultSet resultSet = preparedStatement.executeQuery();
//5.处理结果
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//6.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
- 在src目录下新建jdbc.properties文件
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/db0707?useUnicode=true&characterEncoding=utf8
user = root
password = root
- 工具类的实现
public class JDBCUtils {
//定义成员
private static String url;
private static String user;
private static String password;
private static String driver;
static{
try {
Properties prop = new Properties();
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL urlPath = classLoader.getResource("jdbc.properties");
String path = urlPath.getPath();
prop.load(new FileReader(path));
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
//加载驱动
Class.forName(prop.getProperty("driver"));
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据库连接对象
public static Connection getConnection(){
try {
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//释放资源(DDL、DML)
public static void close(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//针对DQL语句的关闭
public static void close(ResultSet rs,Statement stmt,Connection conn) {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
ORM(Object Relational Mapping)
从数据库查询到的结果集(ResultSet)在进行遍历时,逐行遍历,取出的都是零散的数据。在实际应用开发中,我们需要将零散的数据进行封装整理;
CREATE TABLE `user`(
id INT PRIMARY KEY,#主键
username VARCHAR(20) NOT NULL,
`password` VARCHAR(20) NOT NULL,#非空
sex CHAR(2),
email VARCHAR(50) NOT NULL,
address VARCHAR(20) NOT NULL
)CHARSET = utf8;
INSERT INTO `user` (id,username,`password`,sex,email,address)
VALUES (1001,'kaka','123','男','[email protected]','陕西省');
/**
* 数据库工具类
* 1.获取连接 connection
* 2.释放资源
* 可跨平台方案
*/
import java.io.*;
import java.sql.*;
import java.util.Properties;
public class DBUtils{
//配置文件集合 永远不变
private static final Properties properties = new Properties();
static {
try {
//使用类自带的流
//首次使用工具类,触发类加载
InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
//通过流将配置信息的内容分割成键值对
//将is流中的配置文件信息,加载到集合中
properties.load(is);
Class.forName(properties.getProperty("driver"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException 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();
}
}
}
public class User {
private int id;
private String username;
private String password;
private String sex;
private String email;
private String address;
public User(){}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
", address='" + address + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User(int id, String username, String password, String sex, String email, String address) {
this.id = id;
this.username = username;
this.password = password;
this.sex = sex;
this.email = email;
this.address = address;
}
}
/**
* ORM查询
*/
import java.sql.*;
public class OrmSelect {
public static void main(String[] args) {
Connection connection = DBUtils.getConnection();
String sql = "select id,username,PASSWORD,sex,email,address from `user`;";
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
//拿到每一行数据
while (resultSet.next()) {
//拿到每一列的数据
User user = new User();
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String sex = resultSet.getString("sex");
String email = resultSet.getString("email");
String address = resultSet.getString("address");
//将零散的数据,封装在User对象里
user.setId(id);
user.setUsername(username);
user.setPassword(password);
user.setSex(sex);
user.setEmail(email);
user.setAddress(address);
System.out.println(user);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.closeAll(connection, preparedStatement, resultSet);
}
}
}
DAO(Data Access Object)数据访问对象:将所有对同一张表的操作都封装在一个XXXDaoImpl对象中、根据增删改查的不同功能,实现具体的方法(insert,update,delete,select,selectAll);
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
sex VARCHAR(2),
salary DOUBLE
) CHARSET = utf8;
public class Employee {
private int id;//工号
private String name;//名字
private String sex;//性别
private double salary ;//工资
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "employee [id=" + id + ", name=" + name + ", sex=" + sex + ", salary="
+ salary + "]";
}
public Employee() {
super();
// TODO Auto-generated constructor stub
}
public Employee(int id, String name, String sex, double salary) {
super();
this.id = id;
this.name = name;
this.sex = sex;
this.salary = salary;
}
}
见第7个板块
/*
*对数据库中emp;oyee表的一系列操作(增删改查)
*实现增删改查
*/
public class UserDaoImpl {
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
//增加
public int insert(Employee emp){
try {
//数据库连接对象
conn = JDBCUtils.getConnection();
//编写SQL语句
String sql = "insert into employee (id,name,sex,salary) values(?,?,?,?);";
//预编译对象
stmt = conn.prepareStatement(sql);
//执行更新
stmt.setInt(1, emp.getId());
stmt.setString(2, emp.getName());
stmt.setString(3, emp.getSex());
stmt.setDouble(4, emp.getSalary());
int i = stmt.executeUpdate();
return i;
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.close(stmt, conn);
}
return 0;
}
//删除
public int delete (int id){
conn = JDBCUtils.getConnection();
String sql = "delete from employee where id = ?";
try {
stmt = conn.prepareStatement(sql);
//获取id
stmt.setInt(1, id);
return stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.close(stmt, conn);
}
return 0;
}
//修改
public int update(Employee emp){
conn = JDBCUtils.getConnection();
String sql = "update employee set name = ?,sex = ?,salary = ? where id = ?";
try {
stmt = conn.prepareStatement(sql);
//执行更新
stmt.setString(1, emp.getName());
stmt.setString(2, emp.getSex());
stmt.setDouble(3, emp.getSalary());
stmt.setInt(4, emp.getId());
return stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.close(stmt, conn);
}
return 0;
}
//查询
public List<Employee> selectAll(){
conn = JDBCUtils.getConnection();
String sql = "select id,name,sex,salary from employee";
List<Employee> empList = new ArrayList<Employee>();
try {
stmt = conn.prepareStatement(sql);
rs = stmt.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String sex = rs.getString("sex");
double salary = rs.getDouble("salary");
Employee emp = new Employee(id, name, sex, salary);
//每封装完一个对象,添加到集合当中
empList.add(emp);
}
return empList;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(stmt, conn);
}
return null;
}
}
public class JDBCTest {
public static void main(String[] args) {
//新增
UserDaoImpl udi = new UserDaoImpl();
Employee emp = new Employee(3,"wangwu","nv",4555);
int result = udi.insert(emp);
if(result > 0){
System.out.println("success");
}else{
System.out.println("defeat");
}
//删除
int result = udi.delete(3);
System.out.println(result);
//修改
Employee emp = new Employee(1,"kaka","m",12345);
int result = udi.update(emp);
System.out.println(result);
//查询
List<Employee> empList = udi.selectAll();
empList.forEach(System.out::println);
}
}
java.util.Date
java语言常规应用层面的日期类型;可以通过字符串创建对应的时间对象;无法直接通过JDBC插入数据库
java.sql.Date
不可以通过字符串创建对应的时间对象;只能通过毫秒值创建对象;可以直接通过JDBC插入数据库
sql赋util可以,util赋sql不行,因为sql是util的子类;
- SimpleDateFormat 日期格式化
//自定义一个时间
String str = "2020-02-02";
//日期转换将字符串转为 java.util.Date
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//将日期字符串转换成 util.Date类型
java.util.Date utilDate = sdf.parse(str);
System.out.println(utilDate);
/**
* 日期转换
* 字符串转UtilDate
* 字符串转SqlDate
* utilDate转成SqlDate
*/
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class DateUtils {
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
//字符串转Util
public static java.util.Date strToUtilDate(String str) {
try {
return simpleDateFormat.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
//字符串转sql(不常用)
public static java.sql.Date strToSqlDate(String str){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
java.util.Date date = simpleDateFormat.parse(str);
return new java.sql.Date(date.getTime());
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
//util转sql
public static java.sql.Date utilToSql(java.util.Date date){
return new java.sql.Date(date.getTime());
}
}
public static void main(String[] args) throws ParseException{
//1.java.util.Date
// 当前系统时间
System.out.println(new java.util.Date());
//自定义一个时间
String str = "2020-02-02";
//日期转换将字符串转为 java.util.Date
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//将日期字符串转换成 util.Date类型
java.util.Date utilDate = sdf.parse(str);
System.out.println(utilDate);
//sql.Date 需要毫秒值,来构建一个日期
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
System.out.println(sqlDate);
java.util.Date date = DateUtils.strToUtilDate("2020-03-28");
System.out.println(date);
java.sql.Date date2 = DateUtils.utilToSql(date);
System.out.println(date2);
}
}
使用JDBC操作事务,在对业务进行操作的时候,将业务操作使用事务进行管理,操作结果要么同时成功,要么同时失败;
void setAutoCommit(boolean autoCommit):
参数是 true 或 false 如果设置为 false,表示关闭自动提交,相当于开启事务
void commit():提交事务
void rollback():回滚事务
- 获取连接
- 开启事务
- 获取到 PreparedStatement
- 使用 PreparedStatement 执行两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务
- 关闭资源
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 插入账户
INSERT INTO account (NAME , balance) VALUE ('zhangsan',1000);
INSERT INTO account (NAME , balance) VALUE ('lisi',1000);
public class JDBCDemo {
public static void main(String[] args) {
Connection conn = null ;
PreparedStatement ps1 = null ;
PreparedStatement ps2 = null ;
try {
//获取数据库的连接对象
conn = JDBCUtils.getConnection() ;
//开启事务 //Mysql:中开启 start transaction ;
conn.setAutoCommit(false); //false开启收到提交 ,true自动提交
//准备sql语句
String sqlStr1 = "update account set balance = balance - ? where id = ?" ;
String sqlStr2 = "update account set balance = balance + ? where id = ?" ;
//使用连接对象获取预编译对象,给数据库发送sql进行预编译操作
ps1 = conn.prepareStatement(sqlStr1) ;
ps2 = conn.prepareStatement(sqlStr2) ;
//分别设置参数
ps1.setDouble(1, 500);
ps1.setInt(2, 1);
ps2.setDouble(1, 500);
ps2.setInt(2, 2);
//进行更新
int count1 = ps1.executeUpdate() ;
System.out.println(count1);
System.out.println(sqlStr1);
//模拟中间有问题
int x = 3 / 0 ; //程序问题了,程序结束
int count2 = ps2.executeUpdate() ;
System.out.println(count2);
System.out.println(sqlStr2);
//提交事务
conn.commit();
} catch (SQLException e) {
//回滚 //如果程序问题了,执行catch语句
if(conn!=null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}finally {
JDBCUtils.close(ps1, conn);
JDBCUtils.close(ps2, null);
}
}
}
//开启事务
public static void begin(){
Connection connection = getConnection();
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事务
public static void commit(){
Connection connection = getConnection();
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,null,null);
}
}
//回滚事务
public static void rollback(){
Connection connection = getConnection();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,null,null);
}
}
JDBC每次连接数据库,都要获得一个连接对象。每次创建一个连接对象,都是一个较大的资源,如果在连接量较大的场景下,会极大的浪费资源,容易内存溢出。
连接池就是一个容器,创建好数据库连接对象之后,将这些连接对象存储到容器(初始化)中,每次使用连接对象之后,而是将连接对象归还到连接池中,等待下一次利用;
Java中提供了一个接口DataSource,通过实现该接口,可以创建连接池
使用连接池:c3p0: 需要提供的对应的mysql的c3p0相关的jar包
导入:c3p0-0.9.5.2.jar核心jar包以及它相关的依赖jar包 mchange-commons-java-0.2.12.jar
Druid 是目前比较流行高性能的,分布式列存储
一、亚秒级查询
二、实时数据注入
三、可扩展的PB级存储
四、多环境部署
五、丰富的社区
- 创建druid.properties 配置文件
- 引入druid-1.0.9.jar包
- 需要借助工厂类DruidDataSourceFactory 创建具体的连接池
- 获取DataSouce:连接池对象(接口) 通过连接池获取数据库的连接
- Connection getConnection() throws SQLException;
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db0708?useUnicode=true&characterEncoding=utf8
username=root
password=root
#初始化连接
initialSize=10
#最大连接数量
maxActive=30
#最小空闲连接
minIdle=5
#超时等待时间以毫秒为单位
maxWait=5000
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;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/*
*将Druid连接池的获取通过工具类进行封装使用连接池创建连接对象(每次close()都会归还到连接池中),
*操作DDL,DML,DQL都一样
*/
public class JDBCUtils {
//获取数据库连接池
private static DataSource ds;
//提供静态代码块
//加载配置文件
static{
try{
//创建Properties集合类对象
Properties prop = new Properties();
//加载配置文件
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//加载
prop.load(inputStream);
inputStream.close();
//创建连接池对象
ds = DruidDataSourceFactory.createDataSource(prop);
} catch(IOException e){
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection() ;
}
//定义一个方法,获取存在的连接池
public static DataSource getDataSource() {
return ds ;
}
//释放资源
public static void close(Statement stmt,Connection conn) {
close(null,stmt,conn) ;
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
-- 创建账户表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
)CHARSET = utf8;
-- 插入账户
INSERT INTO account (NAME , balance) VALUE ('张三',1000);
INSERT INTO account (NAME , balance) VALUE ('李四',1000);
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.kaka.utils.JDBCUtils;
public class DruidTest {
public static void main(String[] args) {
Connection conn = null ;
PreparedStatement stmt = null ;
try {
//获取连接
conn = JDBCUtils.getConnection() ;
//准备sql :给account表中插入数据
String sql = "insert into account values(null,?,?)" ;
//获取预编译对象
stmt = conn.prepareStatement(sql) ;
//设置参数
stmt.setString(1, "王五");
stmt.setDouble(2, 3000);
//执行更新
int count = stmt.executeUpdate();
System.out.println("更新成功,影响了"+count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(stmt, conn);
}
}
}