当前位置:首页 > 后端开发 > 正文

java怎么从数据库获取数据

使用JDBC:1. 导入驱动包;2. 获取Connection对象;3. 创建Statement/PreparedStatement;4. 执行executeQuery()获取ResultSet;5. 遍历结果集读取

核心技术体系

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配置示例

java怎么从数据库获取数据  第1张

# 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: 采用以下组合策略解决:

  1. 分页查询SELECT FROM table LIMIT 100000, 100(注意偏移量大时效率低)
  2. 游标机制conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)保持游标打开
  3. 流式处理rs.setFetchSize(Integer.MIN_VALUE)禁用全量加载到内存
  4. 投影优化:仅查询必要字段而非SELECT
  5. 异步处理:将结果写入临时文件/消息队列,后续分段处理
  6. 数据库层面优化:添加合适索引,考虑分区表设计。

0