目录
一、依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
二、获取链接
// JDBC连接的URL, 不同数据库有不同的格式:
String JDBC_URL = "jdbc:mysql://localhost:3306/test";
String JDBC_USER = "root";
String JDBC_PASSWORD = "password";
// 获取连接:
Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
// TODO: 访问数据库...
// 关闭连接:
conn.close();
// 更多使用try()
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
...
}
-
URL
说明 示例 jdbc协议固定前缀 jdbcmysql数据库类型 mysql,postgresqllocalhost数据库服务器地址 localhost(本机)或 IP3306端口号 MySQL 默认 3306 test数据库名 你要连接的库 - USER 每个库有若干USER,认证身份,管理权限,审计追踪
-
设计原则
原则 说明 示例 最小权限 只给必要的权限 只读用户不给写权限 分离用户 不同用途不同用户 查询用 readonly,维护用admin限制来源 指定允许的主机 'user'@'192.168.1.%'而不是'user'@'%'定期改密 密码定期更换 使用密码管理工具 禁止硬编码 代码里不写明文密码 使用环境变量或配置中心 - USER的属性 属性 说明 user用户名 host允许从哪个主机连接( localhost只能本机,%任意主机)password加密存储的密码 privileges拥有的权限 - 命名规范 java // 常见命名方式 "root" // 超级管理员 "admin" // 管理员 "app_java" // Java应用专用 "readonly_user" // 只读用户 "backend_service" // 后端服务用户 "deploy" // 部署用户 -
创建不同场景的用户
语法
sql CREATE USER '用户名'@'主机' IDENTIFIED BY '密码'; GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机'; // 权限列表即SQL语法关键字 // 库名字.*表示所有表例如
```sql -- 1. 只读权限 GRANT SELECT ON test.* TO 'readonly'@'%';
-- 2. 读写权限(增删改查) GRANT SELECT, INSERT, UPDATE, DELETE ON test.* TO 'app'@'%';
-- 3. 表结构管理权限 GRANT CREATE, ALTER, DROP, INDEX ON test.* TO 'developer'@'%';
-- 4. 完整数据库管理权限 GRANT ALL PRIVILEGES ON test.* TO 'admin'@'localhost';
-- 5. 全局管理权限(谨慎使用) GRANT ALL PRIVILEGES ON . TO 'super_admin'@'localhost' WITH GRANT OPTION; ```
-
三、操作
1、查询
- 获取
Connection连接实例 - 编写带占位符
?的 SQL 语句 - 使用
Connection的prepareStatement(sql)方法创建PreparedStatement对象 - 使用
setXxx()方法为占位符设置参数(索引从1开始) - 执行
PreparedStatement对象提供的executeQuery()方法。使用ResultSet接收结果集 - 根据
SELECT列的对应位置或列名来调用getLong()、getString()等方法
-
基础示例
```java // 带占位符的 SQL String sql = "SELECT id, grade, name, gender FROM students WHERE gender = ? AND grade > ?";
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD); PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数(索引从1开始) pstmt.setInt(1, 1); // gender = 1 pstmt.setInt(2, 2); // grade > 2 // 执行查询 try (ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { // 方式1:按索引获取(索引从1开始) long id = rs.getLong(1); long grade = rs.getLong(2); // 方式2:按列名获取(推荐,更清晰) String name = rs.getString("name"); int gender = rs.getInt("gender"); } }} catch (SQLException e) { e.printStackTrace(); } ```
-
对于空值
null```java // 场景1:数据库真的是 0 int age = rs.getInt("age"); // 返回 0 // 场景2:数据库是 NULL int age = rs.getInt("age"); // 因为基本类型没有null,所以也返回 0(无法区分!)
// 为此,使用以下写法 int age = rs.getInt("age"); // 先读取 if (rs.wasNull()) { // 再判断刚才读到的是不是 NULL // 说明数据库里是 NULL } else { // 说明数据库里有值(可能是0,也可能是其他数) } ```
-
类型映射
SQL 数据类型 Java 数据类型 ResultSet 获取方法 PreparedStatement 设置方法 BIT,BOOL,BOOLEANbooleangetBoolean()setBoolean()TINYINT,SMALLINT,INTEGERintgetInt()setInt()BIGINTlonggetLong()setLong()REALfloatgetFloat()setFloat()FLOAT,DOUBLEdoublegetDouble()setDouble()CHAR,VARCHAR,TEXTStringgetString()setString()以下不太常用 比较特殊 比较难记 hhh😄❌ DECIMAL,NUMERICjava.math.BigDecimalgetBigDecimal()setBigDecimal()DATEjava.sql.Date/LocalDategetDate()/getObject()setDate()/setObject()TIMEjava.sql.Time/LocalTimegetTime()/getObject()setTime()/setObject()TIMESTAMP,DATETIMEjava.sql.Timestamp/LocalDateTimegetTimestamp()/getObject()setTimestamp()/setObject()BINARY,BLOBbyte[]getBytes()setBytes()CLOBjava.sql.Clob/StringgetClob()/getString()setClob()/setString()
日期时间
```java // 查询 - 直接获取 LocalDate birthDate = rs.getObject("birth_date", LocalDate.class); LocalDateTime createTime = rs.getObject("create_time", LocalDateTime.class); LocalTime alarmTime = rs.getObject("alarm_time", LocalTime.class);
// 设置参数 pstmt.setObject(1, LocalDate.now()); pstmt.setObject(2, LocalDateTime.now()); pstmt.setObject(3, LocalTime.now()); ```
DECIMAL
```java // 查询 BigDecimal price = rs.getBigDecimal("price"); // 或 BigDecimal price = rs.getObject("price", BigDecimal.class);
// 设置 pstmt.setBigDecimal(1, new BigDecimal("99.99")); // 精度 BigDecimal amount = rs.getBigDecimal("amount", 2); // 保留2位小数 ```
2、插入,更新,删除executeUpdate()
使用executeUpdate() ,返回该次操作的记录数
-
插入
使用
pstmt实例的getGeneratedKeys()方法返回ResultSet之后插入数据的获取主键```java try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) { try (PreparedStatement ps = conn.prepareStatement( "INSERT INTO students (id, grade, name, gender) VALUES (?,?,?,?)")) { ps.setObject(1, 999); // 注意:索引从1开始 ps.setObject(2, 1); // grade ps.setObject(3, "Bob"); // name ps.setObject(4, "M"); // gender int n = ps.executeUpdate(); // 1
// 获取主键 try (ResultSet rs = ps.getGeneratedKeys()) { if (rs.next()) { long id = rs.getLong(1); // 注意:索引从1开始 } } }} ```
-
更新
java try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) { try (PreparedStatement ps = conn.prepareStatement("UPDATE students SET name=? WHERE id=?")) { ps.setObject(1, "Bob"); // 注意:索引从1开始 ps.setObject(2, 999); int n = ps.executeUpdate(); // 返回更新的行数 } } -
删除
java try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) { try (PreparedStatement ps = conn.prepareStatement("DELETE FROM students WHERE id=?")) { ps.setObject(1, 999); // 注意:索引从1开始 int n = ps.executeUpdate(); // 删除的行数 } }
四、事务
基础语法使用try
Connection conn = openConnection();
try {
// 关闭自动提交:
conn.setAutoCommit(false);
// 执行多条SQL语句:
insert(); update(); delete();
// 提交事务:
conn.commit();
} catch (SQLException e) {
// 回滚事务:
conn.rollback();
} finally {
conn.setAutoCommit(true);
conn.close();
}
普通项目可以使用封装来实现简化编写
Spring则只需使用@Transactional 注解
五、批处理
对于一系列只有参数不同的SQL语句的执行,使用专门的批处理而非循环遍历。
- 对于pstmt反复执行
setXxx()操作 - 使用pstmt的
executeBatch()方法,返回一维数组
try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)")) {
// 对同一个PreparedStatement反复设置参数并调用addBatch():
for (Student s : students) {
ps.setString(1, s.name);
ps.setBoolean(2, s.gender);
ps.setInt(3, s.grade);
ps.setInt(4, s.score);
ps.addBatch(); // 添加到batch
}
// 执行batch:
int[] ns = ps.executeBatch();
for (int n : ns) {
System.out.println(n + " inserted."); // batch中每个SQL执行的结果数量
}
}
连接池
-
依赖
xml <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>2.7.1</version> </dependency> -
创建链接池
```java HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); config.setUsername("root"); config.setPassword("password"); config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒 config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒 config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10
// 全局DataSource DataSource ds = new HikariDataSource(config); ```
-
获取与使用连接
```java // 从连接池获取连接(推荐使用 try-with-resources 自动释放) try (Connection conn = ds.getConnection()) { // 执行数据库操作 PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?"); ps.setInt(1, 100); ResultSet rs = ps.executeQuery();
while (rs.next()) { String name = rs.getString("name"); System.out.println(name); } // 自动归还连接到池(无需手动 conn.close())} catch (SQLException e) { e.printStackTrace(); } ```
conn.close()实际是将连接归还到池中,而非真正关闭数据库连接。
-
HikariCP 完整配置参数
```java HikariConfig config = new HikariConfig();
// 基本配置 config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); config.setUsername("root"); config.setPassword("password"); config.setDriverClassName("com.mysql.cj.jdbc.Driver"); // MySQL 8.0 驱动
// 连接池核心参数 config.setMaximumPoolSize(10); // 最大连接数(默认 10) config.setMinimumIdle(5); // 最小空闲连接数(默认同 max) config.setIdleTimeout(600000); // 空闲超时(毫秒,默认 600000,10分钟) config.setConnectionTimeout(30000); // 获取连接超时(毫秒,默认 30000) config.setMaxLifetime(1800000); // 连接最大生命周期(毫秒,默认 1800000,30分钟)
// 连接校验 config.setConnectionTestQuery("SELECT 1"); // 校验 SQL(MySQL) config.setValidationTimeout(5000); // 校验超时(毫秒)
// 性能优化 config.setLeakDetectionThreshold(10000); // 连接泄漏检测阈值(毫秒,0 表示禁用) config.setAutoCommit(false); // 关闭自动提交,手动管理事务 config.setReadOnly(false); // 是否只读模式
config.setPoolName("MyHikariCP"); // 连接池名称(便于监控) ```
tsx 连接数 = (核心数 × 2) + 有效磁盘数量 例如:8 核 CPU + 1 个 SSD → 8×2+1 = 17 个连接
本文由 tazume-sans 原创,转载请注明出处。
评论
0