概述
✅概念
JDBC(JavaDataBaseConnectivityjava数据库连接)是⼀种⽤于执⾏SQL语句的JavaAPI,可以为多种关系型数据库提供
统⼀访问,它是由⼀组⽤Java语⾔编写的类和接⼝组成的。
本质
其实就是java官⽅提供的⼀套规范(接⼝)。⽤于帮助开发⼈员快速实现不同关系型数据库的连接!
功能详解
public class JDBCQuick {
public static void main(String[] args) throws Exception {
//1.注册驱动(可省略)
// Class.forName("com.mysql.cj.jdbc.Driver");
// DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2.获取连接对象
String url = "jdbc:mysql://localhost:3306/itcast";
String user = "root";
String password = "20050606a";
Connection connection = DriverManager.getConnection(url, user, password);
//3.获取执⾏SQL语句的对象
Statement statement = connection.createStatement();
System.out.println("请输⼊员⼯的姓名:");
Scanner scanner = new Scanner(System.in);
String name1 = scanner.nextLine();
//4.编写SQL语句,并执⾏,接受返回的结果集
String sql = "select * from employee where name='"+name1+"'";
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果遍历resultSet结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String workno= resultSet.getString("workno");
String name = resultSet.getString("name");
String gender = resultSet.getString("gender");
int age = resultSet.getInt("age");
String idcard = resultSet.getString("idcard");
Date entrydate = resultSet.getDate("entrydate");
String workaddress = resultSet.getString("workaddress");
System.out.println(id+"\t"+workno+"\t"+name+"\t"+gender+"\t"+age+"\t"+idcard+"\t"+entrydate+"\t"+workaddress);
}
//6.释放资源(先开后关)
resultSet.close();
statement.close();
connection.close();
}
}
public class JDBCPrepareStatement {
public static void main(String[] args) throws Exception {
//1.
注册驱动
(
可省略)
// Class.forName("com.mysql.cj.jdbc.Driver");
// DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2.获取连接对象
String url = "jdbc:mysql://localhost:3306/itcast";
String user = "root";
String password = "20050606a";
Connection connection = DriverManager.getConnection(url, user, password);
//3.获取执⾏SQL语句的对象,防⽌SQL注⼊
PreparedStatement preparedStatement = connection.prepareStatement("select * from employee where name=?");
System.out.println("请输⼊员⼯的姓名:");
Scanner scanner = new Scanner(System.in);
String name1 = scanner.nextLine();
//4.为?占位符赋值,并执⾏SQL语句,接受返回的结果集
preparedStatement.setString(1, name1);
ResultSet resultSet = preparedStatement.executeQuery();
//5.处理结果遍历resultSet结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String workno= resultSet.getString("workno");
String name = resultSet.getString("name");
String gender = resultSet.getString("gender");
int age = resultSet.getInt("age");
String idcard = resultSet.getString("idcard");
Date entrydate = resultSet.getDate("entrydate");
String workaddress = resultSet.getString("workaddress");
System.out.println(id+"\t"+workno+"\t"+name+"\t"+gender+"\t"+age+"\t"+idcard+"\t"+entrydate+"\t"+workaddress);
}
//6.释放资源(先开后关)
resultSet.close();
preparedStatement.close();
connection.close();
}
}
DriverManager驱动管理对象
✅(1)注册驱动:(mysql5以后可直接省略驱动)
1.注册给定的驱动程序:staticvoidregisterDriver(Driverdriver);
2.写代码使⽤:Class.forName(“com.mysql.jdbc.Driver”);
3.在com.mysql.jdbc.Driver类中存在静态代码块
(2)获取数据库连接:
1.获取数据库连接对象:staticConnectiongetConnection(Stringurl,Stringuser,Stringpassword)
2.返回值:Connection数据库连接对象
3.参数
url:指定连接的路径。语法:jdbc:mysql://ip地址(域名):端⼝号/数据库名称
user:⽤⼾名
password:密码
Connection数据库连接对象
✅1.获取执⾏者对象:
获取普通执⾏者对象:StatementcreateStatement0;
获取预编译执⾏者对象:PreparedStatementprepareStatement(Stringsql);
2.管理事务
开启事务:setAutoCommit(booleanautoCommit);参数为false,则开启事务
提交事务:commit();
回滚事务:rollback();
3.释放资源
⽴即将数据库连接对象释放:voidclose();
Statement执⾏sql语句的对象
✅(1)执⾏DML语句:intexecuteUpdate(Stringsql);
返回值int:返回影响的⾏数。
参数sql:可以执⾏insert、update、delete语句。
(2) 执⾏DQL语句:ResultSetexecuteQuery(Stringsql);
返回值ResultSet:封装查询的结果。
参数sql:可以执⾏select语句。
(3)释放资源
⽴即将数据库连接对象释放:voidclose();
ResultSet结果集对象
✅1.判断结果集中是否还有数据:booleannext();
有数据返回true,并将索引向下移动⼀⾏。没有数据返回false。
2.获取结果集中的数据:XXXgetXxx(“列名”);XXX代表数据类型(要获取某列数据,这⼀列的数据类型)。
例如:StringgetString(“name”);
int getInt(" age");
3.释放资源
⽴即将结果集对象释放:voidclose();
ORM
ORM是⼀种思想,ORM(全称为:ObjectRelativeMapping)对象-关系映射
关系数据库是企业级应⽤环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实
体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,⽽在数据库中,关系数据
⽆法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统⼀般以中间件的形式存在,主要实现程序对象到关
系数据库数据的映射。
ORM模型的简单性简化了数据库查询过程。使⽤ORM查询⼯具,⽤⼾可以访问期望数据,⽽不必理解数据库的底层结构。
- 在使⽤JDBC操作数据库时,我们会发现数据都是零散的,明明在数据库中是⼀⾏完整的数据,到了Java中变成了⼀
个⼀个的变量,不利于维护和管理。⽽我们Java是⾯向对象的,⼀个表对应的是⼀个类,⼀⾏数据就对应的是Java
中的⼀个对象,⼀个列对应的是对象的属性,所以我们要把数据存储在⼀个载体⾥,这个载体就是实体类! - ORM(ObjectRelationalMapping)思想,对象到关系数据库的映射,作⽤是在编程中,把⾯向对象的概念
跟数据库中表的概念对应起来,以⾯向对象的⻆度操作数据库中的数据,即⼀张表对应⼀个类,⼀⾏数据对应⼀个对 象,⼀个列对应⼀个属性! - 当下JDBC中这种过程我们称其为⼿动ORM。后续我们也会学习ORM框架,⽐如MyBatis、JPA等。
public class Student {
private Integer stuID;//stu_id
private String stuName;//sut_name
private Integer stuAge;//stu_age
public Student() {
}
public Student(String stuName, Integer stuID, Integer stuAge) {
this.stuName = stuName;
this.stuID = stuID;
this.stuAge = stuAge;
}
@Override
public String toString() {
return "Student{" +
"stuID=" + stuID +
", stuName='" + stuName + '\'' +
", stuAge=" + stuAge +
'}';
}
public Integer getStuID() {
return stuID;
}
public void setStuID(Integer stuID) {
this.stuID = stuID;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public Integer getStuAge() {
return stuAge;
}
public void setStuAge(Integer stuAge) {
this.stuAge = stuAge;
}
}
public class JDBCAdvanced {
@Test
public void testORM() throws SQLException {
Connection connection = DriverManager.getConnection("jdbc:mysql:///itcast", "root", "20050606a");
PreparedStatement preparedStatement = connection.prepareStatement("select * from t_stu where stu_id=?");
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
Student student = null;
if(resultSet.next()) {
int stuId = resultSet.getInt("stu_id");
String stuName = resultSet.getString("stu_name");
int stuAge = resultSet.getInt("stu_age");
//为对象的属性赋值
student=new Student();
student.setStuID(stuId);
student.setStuName(stuName);
student.setStuAge(stuAge);
}
System.out.println(student);
resultSet.close();
preparedStatement.close();
connection.close();
}
@Test
public void testORMList() throws Exception {
Connection connection = DriverManager.getConnection("jdbc:mysql:///itcast", "root", "20050606a");
PreparedStatement preparedStatement = connection.prepareStatement("select * from t_stu ");
ResultSet resultSet = preparedStatement.executeQuery();
Student student = null;
List<Student> list = new ArrayList<Student>();
while(resultSet.next()) {
student = new Student();
int stuId = resultSet.getInt("stu_id")
String stuName = resultSet.getString("stu_name");
int stuAge = resultSet.getInt("stu_age");
//为对象的属性赋值
student=new Student();
student.setStuID(stuId);
student.setStuName(stuName);
student.setStuAge(stuAge);
list.add(student);
}
for(Student stu:list){
System.out.println(stu);
}
resultSet.close();
preparedStatement.close();
connection.close();
}
}
主键回显
✅-在数据中,执⾏新增操作时,主键列为⾃动增⻓,可以在表中直观的看到,但是在Java程序中,我们执⾏完新增后,只
能得到受影响⾏数,⽆法得知当前新增数据的主键值。在Java程序中获取数据库中插⼊新数据后的主键值,并赋值给Java
对象,此操作为主键回显。
public void testReturnPK() throws SQLException {
//获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///itcast", "root", "20050606a");
//预编译SQL语句,告知prapareStatement,返回新增数据的主键列的值
String sql = "insert t_stu (stu_name,stu_age) values(?,?);";
PreparedStatement preparedStatement = connection.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
//创建对象,将对象的属性值,填充到?占位符上(ORM)
Student student = new Student("Bob", null, 21);
preparedStatement.setString(1, student.getStuName());
preparedStatement.setInt(2, student.getStuAge());
//执⾏SQL语句,并获取返回的结果
int i = preparedStatement.executeUpdate();
//处理结果
ResultSet resultSet = null;
if (i > 0) {
System.out.println("成功");
//获取新增的主键列,回显到Student对象的stuId属性上
//返回主键的值,是⼀个单⾏单列的值储存再resultSet⾥
resultSet = preparedStatement.getGeneratedKeys();
if (resultSet.next()) {
int stuId = resultSet.getInt(1);
student.setStuID(stuId);
}
System.out.println(student);
} else {
System.out.println("失败");
}
//关闭资
if (resultSet != null) {
resultSet.close();
}
preparedStatement.close();
connection.close();
}
批量操作
插⼊多条数据时,⼀条⼀条发送给数据库执⾏,效率低下!
通过批量操作,可以提升多次操作效率!
注意:1、必须在连接数据库的URL后⾯追加?rewriteBatchedStatements=true,允许批量操作
2、新增SQL必须⽤values。且语句最后不要追加;结束
3、调⽤addBatch()⽅法,将SQL语句进⾏批量添加操作
4、统⼀执⾏批量操作,调⽤executeBatch()
@Test
public void testBatch() throws SQLException {
//批量操作
Connection connection = DriverManager.getConnection("jdbc:mysql:///itcast?
rewriteBatchedStatements=true", "root", "20050606a");
//预编译SQL语句,告知prapareStatement,返回新增数据的主键列的值
//必须是values,不能加分号
String sql = "insert t_stu (stu_name,stu_age) values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for(int i=1;i<=10000;i++){
preparedStatement.setString(1, "name"+i);
preparedStatement.setInt(2, 1+i);
//使⽤addBatch()
preparedStatement.addBatch();
}
//执⾏批量操作
preparedStatement.executeBatch();
long end = System.currentTimeMillis();
System.out.println("消耗的时间"+(end-start));
preparedStatement.close();
connection.close();
}
SQL注⼊
原理
✅
- sql注⼊攻击:就是利⽤sql语句的漏洞来对系统进⾏攻击
- 按照正常道理来说,我们在密码处输⼊的所有内容,都应该认为是密码的组成
- 但是现在Statement对象在执⾏sql语句时,将⼀部分内容当做查询条件来执⾏了
PreparedStatement的介绍
✅预编译sql语句的执⾏者对象。在执⾏sql语句之前,将sql语句进⾏提前编译
明确sql语句的格式后,就不会改变了。剩余的内容都会认为是参数
参数使⽤?作为占位符
为参数赋值的⽅法:setXxx(参数1,参数2);
参数1:?的位置编号(编号从1开始)
参数2:?的实际参数
执⾏sql语句的⽅法
执⾏insert、update、delete语句:intexecuteUpdate();
执⾏select语句:ResultSetexecuteQuery();
/*
使⽤
PreparedStatement
的登录⽅法,解决注⼊攻击
*/
@Override
public User findByLoginNameAndPassword(String loginName, String password) {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rs = null;
User user = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.创建操作SQL对象
String sql = "SELECT * FROM user WHERE loginname=? AND password=?";
pstm = conn.prepareStatement(sql);
//3.设置参数
pstm.setString(1,loginName);//设置第⼀个?参数
pstm.setString(2,password);//设置第⼆个?参数
System.out.println(sql);
//4.执⾏sql语句,获取结果集
rs = pstm.executeQuery();
//5.获取结果集
if (rs.next()) {
//6.封装
user = new User();
user.setUid(rs.getString("uid"));
user.setUcode(rs.getString("ucode"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setGender(rs.getString("gender"));
user.setDutydate(rs.getDate("dutydate"));
user.setBirthday(rs.getDate("birthday"));
user.setLoginname(rs.getString("loginname"));
}
//7.返回
return user;
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm,rs);
}
}
连接池
概念
✅数据库连接背景
数据库连接是⼀种关键的、有限的、昂贵的资源,这⼀点在多⽤⼾的⽹⻚应⽤程序中体现得尤为突出
对数据库连接的管理能显著影响到整个应⽤程序的伸缩性和健壮性,影响到程序的性能指标
数据库连接池正是针对这个问题提出来的
数据库连接池
数据库连接池负责分配、管理和释放数据库连接
它允许应⽤程序重复使⽤⼀个现有的数据库连接,⽽不是再重新建⽴⼀个
这项技术能明显提⾼对数据库操作的性能
数据库连接池原理
之前的程序:来⼀个访问就会创建⼀个连接,使⽤完了关闭连接。频繁的创建连接和关闭连接是⾮常耗时的。
优化后的程序:提前准备⼀些数据库连接,使⽤的时候从池中获取,⽤完后重新归还给池中。
连接池就是数据库连接对象的缓冲区,通过配置,由连接池负责创建连接、管理连接、释放连接等操作。
预先创建数据库连接放⼊连接池,⽤⼾在请求时,通过池直接获取连接,使⽤完毕后,将连接放回池中,避免了频繁的
创建和销毁,同时解决了创建的效率。
当池中⽆连接可⽤,且未达到上限时,连接池会新建连接。
池中连接达到上限,⽤⼾请求会等待,可以设置超时时间。
常⻅连接池
JDBC的数据库连接池使⽤javax.sql.DataSource接⼝进⾏规范,所有的第三⽅连接池都实现此接⼝,⾃⾏添加具体实
现!也就是说,所有连接池获取连接的和回收连接⽅法都⼀样,不同的只有性能和扩展功能!
- DBCP是Apache提供的数据库连接池,速度相对C3P0较快,但⾃⾝存在⼀些BUG。
- C3P0是⼀个开源组织提供的⼀个数据库连接池,速度相对较慢,稳定性还可以。
- Proxool 是sourceforge下的⼀个开源项⽬数据库连接池,有监控连接池状态的功能,稳定性较c3p0差⼀点
- Druid 是阿⾥提供的数据库连接池,是集DBCP、C3P0、Proxool优点于⼀⾝的数据库连接池,性能、扩展性、易⽤
性都更好,功能丰富。 - Hikari(ひかり[shigali])取⾃⽇语,是光的意思,是SpringBoot2.x之后内置的⼀款连接池,基于BoneCP(已经
放弃维护,推荐该连接池)做了不少的改进和优化,⼝号是快速、简单、可靠。
Druid
硬编码
public void testDruidHardTest() throws SQLException {
/* 硬编码:将连接池的配置信息和Java代码耦合在⼀起。
1、创建HikariDataSource连接池对象
2、设置连接池的配置信息【必须|⾮必须】
3、通过连接池获取连接对象
4、回收连接
*/
DruidDataSource druidDataSource = new DruidDataSource();
//必须设置的配置
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///itcast");
druidDataSource.setUsername("root");
druidDataSource.setPassword("20050606a");
//⾮必须设置的配置
//基于connection的增删改查
druidDataSource.setInitialSize(10);
druidDataSource.setMaxActive(20);
DruidPooledConnection connection = druidDataSource.getConnection();
System.out.println(connection);
connection.close();
}
软编码
✅db.properties
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///itcast
username=root
password=20050606a
initialSize=10
maxActive=20
public void test() throws Exception {
//1.创建Properties集合,⽤于存储外部配置⽂件的key和value值。
Properties properties = new Properties();
//2.读取外部配置⽂件,获取输⼊流,加载到Properties集合⾥。
InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
//3.基于Properties集合构建DruidDataSource连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//4.通过连接池获取连接对象
Connection connection = dataSource.getConnection();
System.out.println(connection);
//5.开发CRUD
//6.回收连接
connection.close();
}
Hikari
硬编码
@Test
public void testHardTestHikari() throws SQLException {
/* 硬编码:将连接池的配置信息和Java代码耦合在⼀起。
1、创建HikariDataSource连接池对象
2、设置连接池的配置信息【必须|⾮必须】
3、通过连接池获取连接对象
4、回收连接
*/
//1.创建HikariDataSource连接池对象
HikariDataSource hikariDataSource = new HikariDataSource();
//2.设置连接池的配置信息【必须 | ⾮必须】
//2.1必须设置的配置
hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariDataSource.setJdbcUrl("jdbc:mysql:///itcast");
hikariDataSource.setUsername("root");
hikariDataSource.setPassword("20050606a");
//2.2 ⾮必须设置的配置
hikariDataSource.setMinimumIdle(10);
hikariDataSource.setMaximumPoolSize(20);
//3.通过连接池获取连接对象
Connection connection = hikariDataSource.getConnection();
System.out.println(connection);
//回收连接
connection.close();
}
软编码
@Test
public void testResourcesHikari() throws Exception {
//1.创建Properties集合,⽤于存储外部配置⽂件的key和value值。
Properties properties = new Properties();
//2.读取外部配置⽂件,获取输⼊流,加载到Properties集合⾥。
InputStream inputStream = HikariTest.class.getClassLoader().getResourceAsStream("hikari.properties");
properties.load(inputStream);
// 3.创建Hikari连接池配置对象,将Properties集合传进去
HikariConfig hikariConfig = new HikariConfig(properties);
// 4. 基于Hikari配置对象,构建连接池
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
// 5.
获取连接
Connection connection = hikariDataSource.getConnection();
System.out.println("connection = " + connection);
//6.
回收连接
connection.close();
}
JDBC优化及⼯具类的封装
JDBC⼯具类封装1.0
package senior.util;
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.SQLException;
import java.util.Properties;
public class JDBCUtil {
//创建连接池引⽤
private static DataSource dataSource;
static {
try{
Properties prop = new Properties();
InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
prop.load(inputStream);
dataSource= DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//对外提供在连接池中获取连接的⽅法
public static Connection getConnection(){
try{
return dataSource.getConnection();
}catch (SQLException e){
throw new RuntimeException(e);
}
}
//对外提供回收连接的⽅法
public static void release(Connection connection){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
JDBC⼯具类封装2.0
package senior.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtilV2 {
//维护⼀个连接池对象,维护了⼀个线程绑定变量的ThreadLocal对象
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
static {
try{
Properties prop = new Properties();
InputStream inputStream = JDBCUtilV2.class.getClassLoader().getResourceAsStream("db.properties");
prop.load(inputStream);
dataSource= DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//对外提供在ThreadLocal中获取连接对象的⽅法
public static Connection getConnection(){
try{
//在ThreadLocal中获取connection
Connection connection= threadLocal.get();
if(connection==null){
connection= dataSource.getConnection();
threadLocal.set(connection);
}
return connection;
}catch (SQLException e){
throw new RuntimeException(e);
}
}
//对外提供回收连接的⽅法,回收过程中,将回收的连接从ThreadLocal中移除!
public static void release(){
try {
Connection connection = threadLocal.get();
if(connection!=null){
threadLocal.remove();
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
Dao封装及DaoBase⼯具类
DaseDAO
package senior.dao;
import senior.util.JDBCUtil;
import senior.util.JDBCUtilV2;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
public class BaseDAO {
//通⽤的增删改⽅法
public int executeUpdate(String sql,Object...params) throws Exception {
Connection connection= JDBCUtilV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params!=null&¶ms.length>0){
for(int i=0;i<params.length;i++){
preparedStatement.setObject(i+1,params[i]);
}
}
int updateCount = preparedStatement.executeUpdate();
preparedStatement.close();
JDBCUtilV2.release();
return updateCount;
}
//通⽤的查询⽅法
public <T> List<T> executeQuery(Class<T> clazz,String sql,Object...params) throws Exception {
Connection connection= JDBCUtilV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params!=null&¶ms.length>0){
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<>();
while(resultSet.next()){
//循环⼀次通过反射创建⼀个对象
T t = clazz.newInstance();
for(int i=1;i<=columnCount;i++){
Object value = resultSet.getObject(i);
//获取列的名字=对象的属性
String filedName = metaData.getColumnLabel(i);
//通过对象和filedName获取要封装的属性
Field field = clazz.getDeclaredField(filedName);
field.setAccessible(true);
field.set(t,value);
}
list.add(t);
}
resultSet.close();
preparedStatement.close();
JDBCUtilV2.release();
return list;
}
//通⽤的查询单个结果的⽅法
public <T> T executeQueryBean(Class<T> clazz,String sql,Object...params) throws Exception {
List<T> list=this.executeQuery(clazz,sql,params);
if(!list.isEmpty()){
return null;
}
return list.get(0);
}
}
StudentDao
package senior.dao;
import senior.pojo.Student;
import java.util.List;
public interface StudentDao {
List<Student> selectAllStudents();
Student selectStudentById(int id);
int insertStudent(Student student);
int updateStudent(Student student);
int deleteStudent(int id);
}
StudentDaoImpl
package senior.dao;
import senior.pojo.Student;
import java.util.List;
public class StudentDaoImpl extends BaseDAO implements StudentDao{
@Override
public List<Student> selectAllStudents() {
String sql="SELECT stu_id stuID ,stu_name stuName,stu_age stuAge from itcast.t_stu";
try {
return executeQuery(Student.class,sql,null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Student selectStudentById(int id) {
String sql="SELECT stu_id stuID ,stu_name stuName,stu_age stuAge from itcast.t_stu where stu_id=?";
try {
return executeQueryBean(Student.class,sql,id);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public int insertStudent(Student student) {
String sql="INSERT INTO itcast.t_stu (stu_name, stu_age) values (?,?)";
try {
return executeUpdate(sql,student.getStuName(),student.getStuAge());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public int updateStudent(Student student) {
String sql="Update itcast.t_stu SET stu_name =? where stu_age=?";
try {
return executeUpdate(sql,student.getStuName(),student.getStuAge());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public int deleteStudent(int id) {
String sql="DELETE FROM itcast.t_stu WHERE stu_id=?";
try {
return executeUpdate(sql,id);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
事务
Show VARIABLES LIKE 'autocommit';
SET autocommit =false;
COMMIT ;
ROLLBACK ;
UPDATE t_bank SET money=money-100 WHERE id=1;
UPDATE t_bank SET money=money+100 WHERE id=2;
CREATE TABLE t_bank(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键',
account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',
money INT UNSIGNED COMMENT '⾦额'
)COMMENT '银⾏账⼾';
INSERT INTO t_bank(account, money) VALUES ('张三',1000);
INSERT INTO t_bank(account, money) VALUES ('李四',1000);
package senior.dao;
import senior.util.JDBCUtil;
import senior.util.JDBCUtilV2;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
public class BaseDAO {
//通⽤的增删改⽅法
public int executeUpdate(String sql,Object...params) throws Exception {
Connection connection= JDBCUtilV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params!=null&¶ms.length>0){
for(int i=0;i<params.length;i++){
preparedStatement.setObject(i+1,params[i]);
}
}
int updateCount = preparedStatement.executeUpdate();
preparedStatement.close();
if(connection.getAutoCommit()){
JDBCUtilV2.release();
}
return updateCount;
}
//通⽤的查询⽅法
public <T> List<T> executeQuery(Class<T> clazz,String sql,Object...params) throws Exception {
Connection connection= JDBCUtilV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params!=null&¶ms.length>0){
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<>();
while(resultSet.next()){
//循环⼀次通过反射创建⼀个对象
T t = clazz.newInstance();
for(int i=1;i<=columnCount;i++){
Object value = resultSet.getObject(i);
//获取列的名字=对象的属性
String filedName = metaData.getColumnLabel(i);
//通过对象和filedName获取要封装的属性
Field field = clazz.getDeclaredField(filedName);
field.setAccessible(true);
field.set(t,value);
}
list.add(t);
}
resultSet.close();
preparedStatement.close();
if(connection.getAutoCommit()){
JDBCUtilV2.release();
}
return list;
}
//通⽤的查询单个结果的⽅法
public <T> T executeQueryBean(Class<T> clazz,String sql,Object...params) throws Exception {
List<T> list=this.executeQuery(clazz,sql,params);
if(!list.isEmpty()){
return null;
}
return list.get(0);
}
}
package senior.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtilV2 {
//维护⼀个连接池对象,维护了⼀个线程绑定变量的ThreadLocal对象
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
static {
try{
Properties prop = new Properties();
InputStream inputStream = JDBCUtilV2.class.getClassLoader().getResourceAsStream("db.properties");
prop.load(inputStream);
dataSource= DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//对外提供在ThreadLocal中获取连接对象的⽅法
public static Connection getConnection(){
try{
//在ThreadLocal中获取connection
Connection connection= threadLocal.get();
if(connection==null){
connection= dataSource.getConnection();
threadLocal.set(connection);
}
return connection;
}catch (SQLException e){
throw new RuntimeException(e);
}
}
//对外提供回收连接的⽅法,回收过程中,将回收的连接从ThreadLocal中移除!
public static void release(){
try {
Connection connection = threadLocal.get();
if(connection!=null){
threadLocal.remove();
connection.setAutoCommit(true);
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}