企业网上书店网站建设设计宁波seo企业网络推广
一、JDBC复习
作业:在 MySQL 中,创建以下数据表,使用 JDBC 连接 MySQL 数据库。同时,实现数据表的增、删、改、查(根据ID查询、分页查询)操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | drop database if exists jdbc_test; create database if not exists jdbc_test ; use jdbc_test; create table `staffs`(`id` int(10) primary key auto_increment,`name` varchar(24) not null comment '姓名',`age` int(10) default 18 comment '年龄',`phone` char(11) comment '联系方式',`sta_pos` varchar(20) comment '职位',`add_time` datetime default current_timestamp comment '入职时间',`update_time` datetime default current_timestamp comment '更新时间' ) engine=innodb charset=utf8 comment '员工记录表'; # 提示:需要自行下载连接MySQL的驱动程序insert into staffs(name,age,phone,sta_pos) values ('张三',18,'13417747371','工程师'); insert into staffs(name,age,phone,sta_pos) values ('李四',19,'13417747372','工程师'); insert into staffs(name,age,phone,sta_pos) values ('王五',20,'13417747373','教授');select * from staffs; |
1、概念
JDBC(Java DataBase Connectity)是Java数据库连接技术的简称,提供连接各种常用数据库的能力;
它提供了操作数据库的接口(规范、标准)。JDBC实现由各数据库厂商提供(驱动程序);
所在包:java.sql
作用:用于连接 + 操作数据库
2、JDBC原理
DriverManager
提供者:sun公司
作用:载入各种不同的JDBC驱动
JDBC 驱动
提供者:数据库厂商
作用:负责连接各种不同的数据库
JDBC<躯壳> VS 驱动程序<灵魂>
3、JDBC的常用对象(API)
1)DriverManager
驱动管理器,管理驱动程序 – 载入各种不同的JDBC驱动程序
2)Connection
连接对象,实现数据库的连接并担任传送数据的任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | package org.zing.util;import java.sql.*;/*** 使用 JDBC 连接 MySQL 数据库** @Author zqx* @Date 2023-03-05*/ public class DbUtil {/*** 连接驱动程序*/private static final String DRIVER = "com.mysql.cj.jdbc.Driver";/*** 连接URL*/private static final String URL = "jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true;characterEncoding=utf8;serverTimezone=Asia/Shanghai";/*** 帐号*/private static final String USER = "root";/*** 密码*/private static final String PASS = "root";static {/** 加载驱动程序*/try {Class.forName(DRIVER);} catch (ClassNotFoundException e) {System.out.println("加载驱动程序失败...");e.printStackTrace();}}/*** 获取连接对象 -- Java程序 与 数据库之间的桥梁** @return*/public static Connection getConnection() {Connection conn = null;try {conn = DriverManager.getConnection(URL, USER, PASS);} catch (SQLException e) {System.out.println("获取连接对象失败...");e.printStackTrace();}return conn;}/*** 关闭相关的 JDBC 对象* <p>* DriverManager:驱动管理对象,获取连接对象* <p>* DriverManager.getConnection(URL, USER, PASS);* <p>* ResultSet:结果集对象 用于接收查询数据时,返回的结果* <p>* Statement:语句对象 用于执行SQL语句(PreparedStatement、CallableStatement)* 增、删、改:executeUpdate() 查询:executeQuery()* <p>* <p>* Connection:连接对象 建立JAVA程序与数据库之间的桥梁** @param rst* @param stmt 父类对象可以接收子类对象 - 多态* @param conn*/public static void close(ResultSet rst, Statement stmt, Connection conn) {if (rst != null) {try {rst.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();}}}public static void main(String[] args) {System.out.println(DbUtil.getConnection());} } |
3)Statement
语句对象,由 Connection 产生、负责执行 SQL 语句
3.1)Statement
1 | Statement stmt = conn.createStatement() ; |
3.2)PreparedStatement
效率高、安全性高、方便
1 2 3 4 5 6 7 8 9 10 | //定义操作数据库的SQL语句->数据由?来占位 String sql = "select * from student where id=?";//预编译SQL语句 PreparedStatement pstmt = conn.prepareStatement(sql);//填充数据,语法如下: // 语句对象.setXxx(占位符索引,数据) -> 从1开始 // Xxx:指是数据类型->Java的数据类型 pstmt.setInt(1, 5); |
3.3)CallableStatement
用于执行存储过程
4、ResultSet
结果集对象,负责保存语句对象执行后所产生的查询结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ResultSet rst = stmt.executeQuery(sql) ; //ResultSet rst = pstmt.executeQuery();//1.循环迭代各行记录 while(rst.next()) { //2.获取每行中,各字段的数据,语法如下://方法一:结果集对象.getXxx(字段索引) -> 从1开始,且是根据select后的字段来获取//方法二:结果集对象.getXxx(字段名称) //注:Xxx指的是字段的数据类型 -> 必须对应Java的数据类型String stuNO = rst.getString(1) ;String cardID = rst.getString(3) ;//int id = rst.getInt("id") ;//String name = rst.getString("name") ;System.out.println(stuNO+"\t"+cardID); } |
5、ResultSetMetaData
元数据对象,可用于获取有关ResultSet
对象中列的类型和属性的信息的对象。
1 2 3 4 5 6 | // 1.实例化对象:结果集对象.getMetaData() ; ResultSetMetaData metaData = rst.getMetaData() ;// 2.常用方法 // 2.1 结果集中包含列的数量:int getColumnCount(): // 2.2获取指定列的别名,索引从1开始:String getColumnLabel(int column) |
二、DbUtils
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate、MyBatis框架的首选。
commons-dbutils API:
- org.apache.commons.dbutils.QueryRunner:提供对sql语句操作的API
- org.apache.commons.dbutils.ResultSetHandler:用于定义select操作后,怎样封装结果集
- org.apache.commons.dbutils.DbUtils:工具类,定义了关闭资源与事务处理的方法
三、QueryRunner
该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
QueryRunner类提供了两个构造方法:
- 默认的构造方法
- 需要一个 javax.sql.DataSource 来作参数的构造方法
常用方法
- public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
- public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource 方法中重新获得 Connection。
- public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。
- public int update(Connection conn, String sql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
- public int update(Connection conn, String sql) throws SQLException:用来执行一个不需要置换参数的更新操作。
四、ResultSetHandler
1.作用
该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式。
2.ResultSetHandler接口的实现类
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ScalarHandler:它是用于单数据。如果:select count(*) from 表操作。
五、数据库连接池
数据库连接池是一种技术,用于管理应用程序与数据库之间的连接。连接池可以有效地减少数据库连接的开销,提高应用程序的性能和可伸缩性。
六、开源的数据源使用
1、DBCP
略
2、C3P0
作者使用了自己喜欢的一个机器人的代号命名
1)使用方法一
第一、引入jar包:c3p0-0.9.5.jar以及依赖的mchange-commons-java-0.2.9.jar
第二、创建ComboPooledDataSource对象
1 | private static ComboPooledDataSource cpds = new ComboPooledDataSource(); |
第三、设置必须的属性
1 2 3 4 | cpds.setDriverClass("com.microsoft.sqlserver.jdbc.SQLServerDriver"); cpds.setJdbcUrl("jdbc:sqlserver://localhost:1433;database=jdbc"); cpds.setUser("sa"); cpds.setPassword("123456"); |
第四、根据情况,设置可选的属性
第五、获取连接对象
1 | Connection conn = ds.getConnection() ; |
第六、关闭连接对象
1 | ds.close() ; |
2)使用方法二
第一、引入jar包:c3p0-0.9.5.jar以及依赖的mchange-commons-java-0.2.9.jar
第二、配置c3p0-config.xml或c3p0.properties文件(存放在构建路径)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?xml version="1.0" encoding="UTF-8"?> <c3p0-config><!-- 默认配置 --><default-config><property name="driverClass">com.mysql.cj.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai</property><property name="user">root</property><property name="password">root</property><!-- 连接池初始化时创建的连接数 --><property name="initialPoolSize">10</property><!-- 连接池保持的最小连接数 --><property name="minPoolSize">10</property><!-- 连接池中拥有的最大连接数 --><property name="maxPoolSize">100</property><!-- 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接 --><property name="maxIdleTime">30</property><!-- 连接池用完,等待时间,设置0无限等待 --><property name="checkoutTimeout">30000</property><!-- 测试空闲连接的间隔时间 --><property name="idleConnectionTestPeriod">30</property><!-- 连接池为数据源缓存的PreparedStatement的总数 --><property name="maxStatements">200</property></default-config><!-- 功能同上,配置另一种情况。需要指定一个名称加以区分:other --><named-config name="other"><property name="driverClass">com.mysql.cj.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai</property><property name="user">root</property><property name="password">root</property><!-- 连接池初始化时创建的连接数 --><property name="initialPoolSize">10</property><!-- 连接池保持的最小连接数 --><property name="minPoolSize">10</property><!-- 连接池中拥有的最大连接数 --><property name="maxPoolSize">100</property><!-- 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接 --><property name="maxIdleTime">30</property><!-- 连接池用完,等待时间,设置0无限等待 --><property name="checkoutTimeout">30000</property><!-- 测试空闲连接的间隔时间 --><property name="idleConnectionTestPeriod">30</property><!-- 连接池为数据源缓存的PreparedStatement的总数 --><property name="maxStatements">200</property></named-config> </c3p0-config> |
第三、创建ComboPooledDataSource对象,并指定配置参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class C3P0Util {/*** 定义C3P0数据源对象 - 使用默认配置创建ComboPooledDataSource对象*/private static ComboPooledDataSource cpds = new ComboPooledDataSource();// 使用某配置创建ComboPooledDataSource对象// private static ComboPooledDataSource cpds = new ComboPooledDataSource("other");public static DataSource getDataSource() {return cpds;}public static Connection getConnection() {try {return cpds.getConnection();} catch (SQLException e) {throw new RuntimeException(e);}} } |
第四、获取连接对象
1 | Connection conn = ds.getConnection() ; |
第五、关闭连接对象
1 | ds.close() ; |
3、Druid
https://github.com/alibaba/druid?tab=readme-ov-file
Druid是什么
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
下载
https://mvnrepository.com/
1 2 3 4 5 | <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid-version}</version> </dependency> |
1)使用方法一
第一、引入jar包
第二、创建ComboPooledDataSource对象
1 | private static DruidDataSource dataSource = new DruidDataSource(); |
第三、设置必须的属性
1 2 3 4 | dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"); dataSource.setUsername("root"); dataSource.setPassword("root"); |
第四、根据情况,设置可选的属性
第五、获取连接对象
1 | Connection conn = dataSource.getConnection() ; |
第六、关闭连接对象
1 | conn.close() ; |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | package org.zing.util;import com.alibaba.druid.pool.DruidDataSource;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;/*** @author zqx* @date 2025-02-24*/ public class DruidHelper {/*** 数据源对象*/private static DruidDataSource dataSource;/*** 获取数据源对象*/public static DataSource getDataSource() {if (dataSource == null) {initDataSource();}return dataSource;}/*** 初始化数据源*/private static void initDataSource() {try {// 实例化数据源对象dataSource = new DruidDataSource();// 基础配置dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");dataSource.setUsername("root");dataSource.setPassword("root");// 初始化连接数量dataSource.setInitialSize(10);// 最小连接数dataSource.setMinIdle(10);// 最大等待时间dataSource.setMaxWait(6000);// 最大连接数dataSource.setMaxActive(100);// 连接有效性检测SQLdataSource.setValidationQuery("SELECT 1");// 空闲时检测连接有效性dataSource.setTestWhileIdle(true);} catch (Exception e) {throw new RuntimeException("初始化Druid连接池失败", e);}}/*** 从连接池获取连接对象*/public static Connection getConnection() {try {return getDataSource().getConnection();} catch (SQLException e) {throw new RuntimeException("获取连接对象失败", e);}}/*** 关闭连接,回收到连接池** @param conn 连接对象*/public static void close(Connection conn) {if (conn != null) {try {conn.close();} catch (SQLException e) {System.err.println("关闭数据库连接时发生异常");}}} } |
2)使用方法二
第一、引入jar包
第二、创建配置文件 druid.properties
文件(存放在构建路径)
1 2 3 4 5 6 7 8 9 10 11 12 | driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username=root password=root # 初始化连接数量 initialSize=10 # 最小连接数 minIdle=10 # 最大连接数 maxActive=100 # 最大等待时间 maxWait=6000 |
第三、创建 DataSource 对象,并指定配置参数
1 2 3 | Properties prop = new Properties(); prop.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties")); DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); |
第四、获取连接对象
1 | Connection conn = dataSource.getConnection() ; |
第五、关闭连接对象
1 | conn.close() ; |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | package org.zing.util;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties;/*** @author zqx* @date 2025-02-24*/ public class DruidUtil {/*** 数据源对象*/private static DataSource dataSource;/*** 获取数据源对象** @return 数据源对象*/public static DataSource getDataSource() {if (dataSource == null) {initDataSource();}return dataSource;}/*** 初始化数据源对象*/private static void initDataSource() {Properties prop = new Properties();try {prop.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));dataSource = DruidDataSourceFactory.createDataSource(prop);} catch (Exception e) {throw new RuntimeException(e);}}/*** 从连接池获取连接对象** @return 连接对象*/public static Connection getConnection() {try {return getDataSource().getConnection();} catch (SQLException e) {throw new RuntimeException("获取连接对象失败", e);}}/*** 关闭连接,回收到连接池** @param conn 连接对象*/public static void close(Connection conn) {if (conn != null) {try {conn.close();} catch (SQLException e) {System.err.println("关闭数据库连接时发生异常");}}}} |
七、DbUtils 增、删、改、查操作
第一:通过C3P0/DBCP获取数据源对象
告知DbUtils操作哪个服务中,哪个端口号的哪个数据库 - 连接池
第二:创建QueryRunner对象
1 | QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()) ; |
第三:定义要操作的SQL语句
1 | String sql = "select id,name,age,phone,pos,add_time as addTime from staffs" ; |
注意:使用别名解决实体对象属性名和数据表字段名不一致的问题
内省
第四:定义SQL语句需要的具体数据
1 | Object[] params = new Object[]{数据1,数据2,...} ; |
第五:执行SQL语句 —— 反射、封装处理
- 查询操作:query(…)
- 更新操作:update(…)
1 2 | List<Staff> list = qr.query(sql, new BeanListHandler<Staff>(Staff.class)); Staff staff = qr.query(sql,new BeanHandler<>(Staff.class),id); |
八、日期时间问题
1、JDBC 类型映射机制
数据库中的时间类型(如 DATETIME
、TIMESTAMP
)通过 JDBC 驱动转换为 Java 类型时,不同版本的驱动处理方式不同:
- MySQL 5.x 驱动:默认将
DATETIME
映射为java.sql.Timestamp
(继承自java.util.Date
) - MySQL 8.x 驱动:支持 JDBC 4.2+,可能直接映射为
java.time.LocalDateTime
当使用 MySQL 8.x 驱动时:
- 数据库
DATETIME
→ JDBC 返回LocalDateTime
BeanProcessor
尝试将LocalDateTime
赋值给java.util.Date
类型属性- 由于无默认转换器,抛出类型不匹配异常
2、解决
1)方案一
使用驱动统一的 java.time
类型
1 2 3 4 5 | public class Staff {// 使用 java.time 类型private LocalDateTime createTime; // getter/setter } |
2)方案二
第一:自定义 BeanProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package org.zing.dao;import org.apache.commons.dbutils.BeanProcessor;import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date;/*** 自定义 BeanProcessor - 实现 LocalDateTime 转换 java.util.Date** @author zqx* @date 2025-02-25*/ public class CustomBeanProcessor extends BeanProcessor {@Overrideprotected Object processColumn(ResultSet resultSet, int index, Class<?> propType) throws SQLException {// 获取某字段的数据Object value = super.processColumn(resultSet, index, propType);// 处理 LocalDateTime → Date 的转换if (value instanceof LocalDateTime localDateTime && propType == Date.class) {return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());}// 其它类型的转换...return value;} } |
第二:通过 RowProcessor
注入自定义处理器
1 2 3 4 5 6 7 8 | // 创建自定义 RowProcessor BasicRowProcessor rowProcessor = new BasicRowProcessor(new CustomBeanProcessor());// 创建 BeanListHandler 时传入自定义处理器 BeanListHandler<Staff> handler = new BeanListHandler<>(Staff.class, rowProcessor);// 执行查询 List<Staff> list = runner.query("select * from staffs", handler); |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public List<Staff> selectByPage(int limit, int count) {String sql = "select id,name,age,phone,sta_pos as staPos,add_time as addTime,update_time as updateTime from staffs limit ?,?";List<Staff> list = null;try {// ResultSetHandler VS BeanListHandler// ResultSetHandler 结果集处理器,通过实现此接口来对结果集数据进行处理// BeanListHandler 是 ResultSetHandler 接口的其中一个实现类,返回实体对象的List集合Object[] params = {limit, count};// 创建自定义 RowProcessorBasicRowProcessor rowProcessor = new BasicRowProcessor(new CustomBeanProcessor());// 创建 BeanListHandler 时传入自定义处理器BeanListHandler<Staff> handler = new BeanListHandler<>(Staff.class, rowProcessor);// 执行查询list = qr.query(sql, handler, params);} catch (SQLException e) {throw new RuntimeException(e);}return list; } |