简单了解JDBC相关操作
JDBC
1、概念
JDBC就是使用java语言操作关系型数据库的一套API,全称java数据库连接。
2、具体步骤
- 创建工程,导入驱动jar包
- 注册驱动
- 获取连接
- 定义SQL语句
- 获取执行SQL语句的对象
- 执行SQL
- 处理返回结果
- 释放资源
@Test public void jdbcTest() throws ClassNotFoundException, SQLException { //获取驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //建立连接 String url = "jdbc:mysql://localhost:3306/test"; Connection connection = DriverManager.getConnection(url, "root", "root"); //编写SQL语句 String sql = "select * from student"; //获取执行SQL的对象 Statement statement = connection.createStatement(); //执行SQL ResultSet resultSet = statement.executeQuery(sql); //处理返回结果 while(resultSet.next()){ System.out.print(resultSet.getInt("id") + "\t"); System.out.print(resultSet.getString("name") + "\t"); System.out.println(resultSet.getString("num")); System.out.println("-------------------------"); } //释放资源 resultSet.close(); statement.close(); connection.close(); }
3、API详解
3.1、DriverManager
3.1.1、注册驱动
在上面我们发现,我们注册驱动的时候用的是这行代码
Class.forName("com.mysql.cj.jdbc.Driver");//加载Driver类
我们来看一下Driver的源码,在源码中用一个静态代码块
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
那么我们是不是可以这样来注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
我们可以参考 简单了解java代码块,发现静态代码块随着类的加载而加载,上面的写法程序可以运行成功,但是没有必要,因为执行以上代码后,相当于new了两次Driver。我们只需要加载一次Driver类静态代码块就会自动执行,成功注册驱动。
补充:MySQL 5之后的驱动包,可以省略注册驱动的步骤
3.1.2、创建连接
类 | 方法 |
---|---|
static Connection | getConnection(String url, String name, String password) |
参数说明:
- url:连接路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…
示例:jdbc:mysql://127.0.0.1:3306/test
细节:
- 如果连接的是本机的mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名?参数键值对1/参数键值对2…
- 如果数据出现乱码需要加上参数:?useUnicode=true&characterEncoding=utf8,表示数据库中的数据以UTF8编码处理数据。
- name:用户名
- password:账户密码
3.2、Connection
3.2.1、作用
- 获取执行SQL的对象
普通执行SQL
Statement createStatement()
预编译SQL的执行SQL对象:防止SQL注入
PrepareStatement prepareStatement(sql)
补充:SQL注入
1、什么是SQL注入
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。简单来说就是用户在界面提交数据时,人为的添加一些特殊字符,使得sql语句结构发生了变化,最终在没有用户或者密码的情况下进行登录。
2、PreparedStatement解决SQL注入方案
PreparedStatement是Statement的子接口,可以防止SQL注入问题。可以根据Connection接口中的prepareStatement(sql)方法获得PreparedStatement对象。
注意:sql提前创建好的,sql语句中需要参数。使用?进行占位,比如:
select * from user where username = ? and password = ?;
3、操作步骤
步骤一:
PreparedStatement pstmt = conn.prepareStatement(sql);
步骤二:设置参数(执行sql之前):pstmt.setXxx(int index, 要放入的值),根据不同类型的数据进行方法选择。
参数说明:
第一个参数:int index表示的是问号出现的位置,问号是从1开始计数的
第二个参数:给问号的位置传入值
/** * PreparedStatement */ public class JDBCDemo1 { @Test public void jdbcTest() throws ClassNotFoundException, SQLException { //获取驱动 //Class.forName("com.mysql.cj.jdbc.Driver"); DriverManager.registerDriver(new Driver()); //建立连接 String url = "jdbc:mysql://localhost:3306/test"; Connection connection = DriverManager.getConnection(url, "root", "root"); //编写SQL语句 String sql = "select * from student where id = ? or name = ?"; //获取执行SQL的对象 PreparedStatement pstmt = connection.prepareStatement(sql); pstmt.setInt(1, 1); pstmt.setString(2, "糖魅"); //执行SQL ResultSet rs = pstmt.executeQuery(); //处理返回结果 while(rs.next()){ System.out.print(rs.getInt("id") + "\t"); System.out.print(rs.getString("name") + "\t"); System.out.println(rs.getString("num")); System.out.println("-------------------------"); } //释放资源 rs.close(); pstmt.close(); connection.close(); } }
- 事务管理
MySQL事务管理
开启事务:BEGIN; START TRANSACTION
提交事务:COMMIT;
回滚事务:ROLLBACK
MySQL默认自动提交事务
JDBC事务管理:Connection接口定义了三个对应的方法
开启事务:SetAutoCommit(boolean autoCommit):true为自动提交事务、false为手动提交事务,即开启事务
提交事务:commit()
回滚事务:rollback()
3.3、Statement
3.3.1、作用
执行SQL语句,具体为一下两种:
-
int executeUpdate(sql):执行DML、DDL语句
返回值:
1、DML语句影响的行数
2、DDL语句执行后,执行成功返回0
-
ResultSet executeQuery(sql):执行DQL语句
返回值:ResultSet结果集对象
3.4、ResultSet
3.4.1、原理
- ResultSet内部有一个指针,刚开始记录开始位置
- 调用next方法,ResultSet内部指针会移动到下一行数据
- 我们可以通过ResultSet得到一行数据getXxxx得到某列数据(例如getInt、getString等)
3.4.2、使用步骤
- 游标向下移动一行,并判断该行是否有数据:next()
- 获取数据:getXxx(参数)
while(rs.next){ rs.getXxx(参数); }
4、JDBC工具类的编写和使用
抽取其他jdbc代码中的重复代码
示例:
工具类:
public class JDBCUtil { //注册驱动所需参数 private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; //建立连接所需参数 private static final String URL = "jdbc:mysql://localhost:3306/test"; private static final String NAME = "root"; private static final String PASSWORD = "root"; //注册驱动 static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } //建立连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, NAME, PASSWORD); } //释放资源 public static void close(Statement stmt, Connection conn) { try { if (stmt != null) { stmt.close(); } } catch (SQLException e) { throw new RuntimeException(e); } try { if (conn != null) { conn.close(); } } catch (SQLException e) { throw new RuntimeException(e); } } public static void close(ResultSet rs, Statement stmt, Connection conn) { try { if (rs != null) { rs.close(); } } catch (SQLException e) { throw new RuntimeException(e); } try { if (stmt != null) { stmt.close(); } } catch (SQLException e) { throw new RuntimeException(e); } try { if (conn != null) { conn.close(); } } catch (SQLException e) { throw new RuntimeException(e); } } }
运用工具类:
public class JDBCDemo1 { @Test public void jdbcTest() throws ClassNotFoundException, SQLException { //注册驱动并获取连接 Connection connection = JDBCUtil.getConnection(); //编写SQL语句 String sql = "select * from student where id = ? or name = ?"; //获取执行SQL的对象 PreparedStatement pstmt = connection.prepareStatement(sql); pstmt.setInt(1, 1); pstmt.setString(2, "糖魅"); //执行SQL ResultSet rs = pstmt.executeQuery(); //处理返回结果 while (rs.next()) { System.out.print(rs.getInt("id") + "\t"); System.out.print(rs.getString("name") + "\t"); System.out.println(rs.getString("num")); System.out.println("-------------------------"); } //释放资源 JDBCUtil.close(rs, pstmt, connection); } }
5、三层架构模型
5.1、开发中常用的三层架构模型
web层
- 接收客户发送的信息
- 把接收的数据封装成对象
- 调用Service层方法并传递数据对象
- 接收Service层方法对数据处理的结果,向客户进行反馈
Service层
处理业务逻辑并调用Dao层的方法
Dao层
和数据库交互(基层利用jdbc技术)
5.2、分层的目的
解耦
就是降低代码之间的依赖关系
可维护性
那一层出问题,直接找那一层
可扩展性
那一层需要添加代码,直接添加即可
可重用性
一个方法可以被其他层重复利用
模拟示例:
模拟web层
public class FindStudent { @Test public void test(){ //创建Student类 Student student = new Student(); String name = "糖解"; String num = "2021110614"; boolean judge = false; //将数据进行封装 student.setName(name); student.setNum(num); //判断是否存在该学生 judge = StudentService.findStudent(student); if(judge){ System.out.println("该学生存在~"); }else{ System.out.println("不存在该学生"); } } }
模拟service层
public class StudentService { public static boolean findStudent(Student student){ Student stu = StudentDao.res(student); if(stu != null){ return true; } return false; } }
模拟dao层
public class StudentDao { public static Student res(Student student){ //System.out.println(student.getName() + " " + student.getName()); Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; Student stu = null; try { //创建数据库连接 conn = JDBCUtil.getConnection(); //编写sql String sql = "select * from student where name = ? and num = ?"; //执行sql pstmt = conn.prepareStatement(sql); pstmt.setString(1,student.getName()); pstmt.setString(2,student.getNum()); //处理结果 rs = pstmt.executeQuery(); if(rs != null && rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); String num = rs.getString("num"); stu = new Student(id, name, num); } } catch (SQLException e) { throw new RuntimeException(e); }finally { JDBCUtil.close(rs, pstmt, conn); } return stu; } }
6、数据库连接池
6.1、概述
- 数据库连接池是一个容器,负责分配、管理数据库连接
- 它允许程序重复使用一个现有的数据库连接,而不是在重新创建一个
- 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
- 好处:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
6.2、实现
6.2.1、标准接口:DataSource
Connection getConnection();
6.2.2、学习:Druid(德鲁伊)
1.基本使用
创建数据库连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(properties对象);
我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。
Druid连接池的配置文件名字随意,放到src目录或者项目根目录下加载“druid.properties”文件内容(示例):
#配置数据库连接相关参数 driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useSSL=false username=root password=root #配置连接池相关参数 filters=stat initialSize=2 maxActive=300 maxWait=60000 timeBetweenEvictionRunsMillis=60000 minEvictableIdleTimeMillis=300000 validationQuery=SELECT 1 testWhileIdle=true testOnBorrow=false testOnReturn=false poolPreparedStatements=false maxPoolPreparedStatementPerConnectionSize=200
如何加载配置文件呢?请看下方:
//InputStream in = DruidDemo.class.getClassLoader().getResourceAsStream("druid,properties"); //Properties prop = new Properties(); //prop.load(in); Properties prop = new Properties(); prop.load(new FileInputStream("druid.properties"));
从数据库连接池中,获取Connection对象
Connection conn = ds.getConnection();
接下来就是jdbc的常规操作了
public class DruidDemo { @Test public void druidTest() throws Exception { //导入jar包 //加载properties文件 Properties prop = new Properties(); prop.load(new FileInputStream("druid.properties")); //利用Druid工厂类,获取数据库连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //从连接池中获取一个Connection对象 Connection conn = dataSource.getConnection(); //编写sql语句 String sql = "select * from student"; //获取执行sql的对象 PreparedStatement pstm = conn.prepareStatement(sql); //执行sql ResultSet rs = pstm.executeQuery(); //处理结果 while(rs.next()){ System.out.print(rs.getInt("id") + " "); System.out.print(rs.getString("name") + " "); System.out.println(rs.getString("num")); System.out.println("---------------------"); } //释放资源 rs.close(); pstm.close(); //将连接归还连接池 conn.close(); } }
-
- 事务管理