Java与数据库:JDBC详解
JDBC
JDBC(Java DataBase Connectivity)是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。
JDK自带了一个java.sql包,而这里面就定义了大量的接口,不同类型的数据库,都可以通过实现此接口,编写适用于自己数据库的实现类。
不同的数据库厂商实现的这套标准,称为数据库驱动。
使用JDBC连接数据库
// 1. 通过DriverManager来获得数据库连接
try (Connection connection = DriverManager.getConnection("连接URL","用户名","密码");
// 2. 创建一个用于执行SQL的Statement对象
Statement statement = connection.createStatement()){ //注意前两步都放在try()中,因为在最后需要释放资源!
// 3. 执行SQL语句,并得到结果集
ResultSet set = statement.executeQuery("select * from 表名");
// 4. 查看结果
while (set.next()){
...
}
}catch (SQLException e){
e.printStackTrace();
}
// 5. 释放资源,try-with-resource语法会自动帮助我们closeStatement的3个方法
executeQuery()方法来执行select语句,此方法返回给我们一个ResultSet对象。
executeUpdate()方法来执行一个DML或是DDL语句,它会返回一个int类型,表示执行后受影响的行数,可以通过它来判断DML语句是否执行成功。
excute()来执行任意的SQL语句,它会返回一个boolean来表示执行结果是一个ResultSet还是一个int,然后使用getResultSet()或getUpdateCount()来获取需要的结果。
批处理
public static void main(String[] args) throws ClassNotFoundException {
try (Connection connection = DriverManager.getConnection();
Statement statement = connection.createStatement()) {
statement.addBatch("insert into user values ('f', 1234)");
statement.addBatch("insert into user values ('e', 1234)"); //添加每一条批处理语句
statement.executeBatch(); //一起执行
} catch (SQLException e){
e.printStackTrace();
}
}将查询结果映射为对象
public List<User> getAllUsers() {
String sql = "SELECT id, username, email, age FROM users";
List<User> userList = new ArrayList<>();
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
// 手动映射:从ResultSet中取出数据,set到User对象中
User user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
user.setAge(rs.getInt("age"));
// 如果有日期字段
user.setCreateTime(rs.getTimestamp("create_time"));
userList.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return userList;
}利用反射将查询结果映射为对象
1. 注解定义
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
}2. 实体类
public class User {
@Column(name = "id")
private Long id;
@Column(name = "username")
private String username;
@Column(name = "email")
private String email;
@Column(name = "age")
private Integer age;
// 必须有无参构造
public User() {}
// getter/setter (省略)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
}3. 核心映射器
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class SimpleMapper {
public static <T> List<T> mapToList(ResultSet rs, Class<T> clazz) throws Exception {
List<T> list = new ArrayList<>();
while (rs.next()) {
T obj = clazz.getDeclaredConstructor().newInstance();
// 遍历所有字段
for (Field field : clazz.getDeclaredFields()) {
Column col = field.getAnnotation(Column.class);
if (col != null) {
field.setAccessible(true);
Object value = rs.getObject(col.name());
field.set(obj, value);
}
}
list.add(obj);
}
return list;
}
}4. 使用示例
public List<User> getAllUsers() {
String sql = "SELECT id, username, email, age FROM users";
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc-learn", "root", "root");
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
// 一行代码完成映射
return SimpleMapper.mapToList(rs, User.class);
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}核心原理:
- 通过反射获取实体类的所有字段
- 读取字段上的
@Column注解获取数据库列名 - 从
ResultSet中取出对应列的值 - 通过反射设置到对象字段中
使用PreparedStatement
如果单纯地使用Statement来执行SQL命令,会存在严重的SQL注入攻击漏洞。我们可以使用PreparedStatement来解决,在如下代码中,我们输入的参数在最终sql语句中会被引号包裹:
public static void main(String[] args) throws ClassNotFoundException {
try (Connection connection = DriverManager.getConnection("URL","用户名","密码");
PreparedStatement statement = connection.prepareStatement("select * from user where username= ? and pwd=?;");
Scanner scanner = new Scanner(System.in)) {
statement.setString(1, scanner.nextLine());
statement.setString(2, scanner.nextLine());
System.out.println(statement);
ResultSet res = statement.executeQuery();
while (res.next()) {
String username = res.getString(1);
System.out.println(username+" 登陆成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}
}管理事务
JDBC默认的事务处理行为是自动提交,使用事务时,我们需要手动关闭自动提交。
con.setAutoCommit(false); //关闭自动提交相当于开启事务
// SQL语句
// SQL语句
// SQL语句
con.commit();或 con.rollback();我们也可以去创建一个回滚点Savepoint savepoint = connection.setSavepoint(); 来实现定点回滚:
public static void main(String[] args) throws ClassNotFoundException {
try (Connection connection = DriverManager.getConnection("URL","用户名","密码");
Statement statement = connection.createStatement()){
connection.setAutoCommit(false); //关闭自动提交,现在将变为我们手动提交
statement.executeUpdate("insert into user values ('a', 1234)");
Savepoint savepoint = connection.setSavepoint(); //创建回滚点
statement.executeUpdate("insert into user values ('b', 1234)");
connection.rollback(savepoint); //回滚到回滚点,撤销前面全部操作
statement.executeUpdate("insert into user values ('c', 1234)");
connection.commit(); //提交事务(回滚之前的内容都没了)
}catch (SQLException e){
e.printStackTrace();
}
} 版权申明
本文系作者 @xiin 原创发布在To Future$站点。未经许可,禁止转载。
暂无评论数据