核心技术体系
Java与数据库交互的核心依赖于JDBC(Java Database Connectivity)标准接口,该规范定义了一套统一的API用于连接各类关系型数据库,实际开发中需结合以下组件协同工作:
| 层级 | 作用 | 典型代表 |
|————-|——————————-|—————————|
| 驱动层 | 实现JDBC接口的具体数据库适配 | MySQL Connector/J |
| 连接管理层 | 维护数据库物理连接 | Druid/HikariCP连接池 |
| SQL执行层 | 解析并执行SQL语句 | Statement/PreparedStatement|
| 结果处理层 | 封装查询结果 | ResultSet/实体对象映射 |
| ORM层 | 对象-关系映射 | MyBatis/Hibernate/JPA |
基础实现流程(基于纯JDBC)
环境准备
- 添加依赖:根据目标数据库选择对应驱动包(如mysql-connector-java-8.0.xx.jar)
- 注册驱动:通过
Class.forName("com.mysql.cj.jdbc.Driver")
显式加载驱动类 - 构建连接URL:格式为
jdbc:mysql://host:port/dbname?useSSL=false&serverTimezone=UTC
标准操作五步曲
// 1. 加载驱动(新版JDBC可省略此步) Class.forName("com.mysql.cj.jdbc.Driver"); // 2. 建立连接 Connection conn = DriverManager.getConnection(url, user, password); // 3. 创建执行器(推荐使用PreparedStatement防SQL注入) String sql = "SELECT FROM users WHERE age > ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 18); // 设置参数值 // 4. 执行查询 ResultSet rs = pstmt.executeQuery(); // 5. 遍历结果集 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("username"); // ...其他字段处理 } // 6. 释放资源(务必按反向顺序关闭) rs.close(); pstmt.close(); conn.close();
关键注意事项
风险点 | 解决方案 |
---|---|
SQL注入漏洞 | ✅ 强制使用PreparedStatement ❌ 禁止字符串拼接SQL |
空指针异常 | 🔍 调用rs.getXXX() 前先用rs.wasNull() 判断 |
大数据量内存溢出 | 🔄 启用流式读取(statement.setFetchSize(Integer.MIN_VALUE) ) |
字符编码问题 | ⚙️ URL参数添加characterEncoding=UTF-8 |
事务控制缺失 | 🔄 conn.setAutoCommit(false) + 手动提交/回滚 |
连接池优化方案
生产环境必须使用连接池替代直连模式,主流实现对比如下:
| 特性 | Druid | HikariCP | C3P0 |
|———————|——————————–|——————————|—————————|
| 性能表现 | ★★★☆ | ★★★★★(目前最快) | ★★★☆ |
| 监控功能 | 内置Web监控面板 | 基础指标 | 较弱 |
| 配置复杂度 | 中等 | 极简(默认配置即可生效) | 较高 |
| 特殊功能 | SQL防火墙/慢查询日志 | 无感集成Spring Boot | JMX支持 |
Druid配置示例:
# application.properties spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 连接池核心参数 spring.datasource.initial-size=5 spring.datasource.max-active=20 spring.datasource.min-idle=5 spring.datasource.max-wait=60000 # 超时时间毫秒
ORM框架应用实践
MyBatis基础用法
Mapper接口定义:
public interface UserMapper { User selectById(@Param("id") Long id); List<User> selectAll(); }
XML映射文件:
<!-UserMapper.xml --> <select id="selectById" resultType="com.example.User"> SELECT FROM users WHERE id = #{id} </select> <select id="selectAll" resultMap="userMap"> SELECT u., r.role_name AS roleName FROM users u LEFT JOIN roles r ON u.role_id = r.id </select>
动态SQL示例:
<select id="findUsers" parameterType="map" resultType="User"> SELECT FROM users <where> <if test="name != null">AND name LIKE CONCAT('%',#{name},'%')</if> <if test="minAge != null">AND age >= #{minAge}</if> </where> ORDER BY create_time DESC <if test="startIndex != null and pageSize != null"> LIMIT #{startIndex},#{pageSize} </if> </select>
JPA/Hibernate核心概念
术语 | 说明 | 示例用法 |
---|---|---|
@Entity | 标识持久化类 | @Entity(name="users") |
@Table | 指定表名/约束 | @Table(uniqueConstraints=...) |
@Column | 列属性映射 | @Column(nullable=false) |
HQL | 面向对象的查询语言 | FROM User u WHERE u.age > 18 |
Criteria API | 类型安全的动态查询 | cb.like(root.get("name"), "%张%") |
复杂场景解决方案
批量数据处理
方法 | 适用场景 | 性能特征 | 示例代码 |
---|---|---|---|
AddBatch | 单次插入少量数据(<500) | 较快 | pstmt.addBatch(); |
RewriteBatchedStatement | 大批量插入(>1万条) | 极快(减少网络IO) | ((com.mysql.jdbc.StatementImpl)pstmt).enableBatchToHack(); |
Stream API | Java 8+函数式处理 | 低内存占用 | resultSet.stream().forEach(row -> process(row)); |
分布式事务处理
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
XA两阶段提交 | 强一致性 | 性能差,锁时间长 | 金融交易等强一致场景 |
TCC事务 | 最终一致性,性能好 | 需实现Try/Confirm/Cancel接口 | 电商订单系统 |
Saga模式 | 异步解耦,高可用 | 补偿机制复杂 | 跨服务业务流程 |
常见错误排查指南
错误现象 | 可能原因 | 解决方案 |
---|---|---|
Communications link failure | 网络中断/数据库重启 | 检查网络连通性,重试机制 |
Access denied for user ‘root’@’host’ | 权限不足/密码错误 | 授予相应权限,核对凭证 |
The column index is out of range | 结果集元数据不匹配 | 检查SELECT字段与Java类型的映射 |
Too many connections | 未使用连接池/最大连接数超限 | 启用连接池,调整maxActive参数 |
Data truncation | 数据类型转换错误 | 检查字段长度/精度匹配 |
相关问答FAQs
Q1: 如何防止SQL注入攻击?
A: 最有效的方法是始终使用PreparedStatement
代替Statement
,并通过问号占位符(?)传递参数。
// 安全做法 String sql = "SELECT FROM users WHERE email = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, userInputEmail); // 自动转义特殊字符 // 危险做法(易受注入) Statement stmt = conn.createStatement(); String badSql = "SELECT FROM users WHERE email = '" + userInputEmail + "'"; // 若输入含' OR '1'='1则会被执行
补充措施:①对输入进行白名单校验 ②启用数据库防火墙 ③定期审计慢查询日志。
Q2: 查询百万级数据时出现OOM怎么办?
A: 采用以下组合策略解决:
- 分页查询:
SELECT FROM table LIMIT 100000, 100
(注意偏移量大时效率低) - 游标机制:
conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)
保持游标打开 - 流式处理:
rs.setFetchSize(Integer.MIN_VALUE)
禁用全量加载到内存 - 投影优化:仅查询必要字段而非
SELECT
- 异步处理:将结果写入临时文件/消息队列,后续分段处理
- 数据库层面优化:添加合适索引,考虑分区表设计。
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/105438.html