七,JDBC_API和Spring_Data_JPA之间的爱恨情仇

两者之间的比较

Spring Data JPA 和 JDBC API 是两种常用的数据库访问技术,它们的使用方式和特点存在明显的差异。下面我们来详细说明 Spring Data JPAJDBC API 的使用区别。

1. 开发复杂度与抽象层次

  • Spring Data JPA:Spring Data JPA 是基于 JPA(Java Persistence API)的高级 ORM(对象关系映射)框架,它通过 实体类 映射数据库中的表,自动生成 SQL 语句,简化了开发人员对 SQL 的管理和数据库操作的繁琐步骤。JPA 将数据库表映射为 Java 对象,简化了开发过程中的许多细节操作。
  • JDBC API:JDBC 是 Java 提供的底层数据库访问标准接口,开发人员必须手动编写 SQL 语句,处理数据库连接、执行查询以及结果集的处理。JDBC API 更加直接但复杂,需要处理很多细节。

对比:Spring Data JPA 提供了更高的抽象层次,简化了 SQL 和数据库管理,而 JDBC API 则提供了更低层次的灵活性,要求开发者自行编写 SQL。

2. 数据库操作

  • Spring Data JPA:通过定义实体类和仓库接口来执行数据库操作,常用的 CRUD 操作不需要写 SQL,Spring Data JPA 会自动生成查询逻辑。你可以通过 Repository 接口 提供的许多标准方法(如 save(), findById(), delete() 等)来完成数据库操作。
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}

public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
}

在上面的代码中,通过继承 JpaRepository,你无需编写具体的 SQL 语句,Spring Data JPA 会自动根据命名规范生成 SQL。

  • JDBC API:在 JDBC 中,开发者需要手动编写 SQL 语句,管理数据库连接和执行查询。常见的数据库操作使用 JdbcTemplate,它简化了一些重复性操作,但依然需要开发者明确编写 SQL 语句。
public class UserRepository {

@Autowired
private JdbcTemplate jdbcTemplate;

public List<Map<String, Object>> findAllUsers() {
String sql = "SELECT * FROM users";
return jdbcTemplate.queryForList(sql);
}

public void addUser(String name, String email) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
jdbcTemplate.update(sql, name, email);
}
}

在使用 JDBC 时,SQL 语句完全由开发人员控制,查询、插入、更新和删除操作都依赖明确的 SQL。

对比:Spring Data JPA 通过自动生成 SQL 提供了较高的抽象,简化了数据操作;而 JDBC API 则更灵活,适合需要精确控制 SQL 语句的场景。

3. 对象关系映射(ORM) vs 直接 SQL 操作

  • Spring Data JPA:JPA 是一个 ORM 框架,支持将数据库表映射为 Java 对象,开发人员只需要处理对象,而不需要直接管理数据库表。JPA 提供了一些复杂操作的便捷性,例如级联操作、延迟加载等。
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders;
- **自动生成表**:JPA 可以根据实体类自动生成数据库表结构。
- **关联关系**:JPA 提供了 @OneToMany、@ManyToOne 等注解来简化对象间的关系映射。
  • JDBC API:JDBC 是原生的数据库操作方式,不提供任何对象与关系的映射。开发人员必须显式编写 SQL 并处理结果集,自己负责将查询结果映射为 Java 对象。这虽然灵活,但当涉及复杂关联关系时,管理工作可能会变得非常繁琐。
public List<User> getUsers() {
return jdbcTemplate.query("SELECT * FROM users",
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")));
}

对比:Spring Data JPA 提供了更高层次的 ORM 支持,适合对复杂数据模型和关系操作的项目。JDBC 则适合需要直接控制 SQL 的应用。

4. 性能与控制

  • Spring Data JPA:由于 JPA 需要将数据库表与 Java 对象进行映射,使用 JPA 在复杂查询、批量操作时可能性能不如 JDBC 高效。并且有时候,自动生成的 SQL 可能无法完全优化,尤其是对于复杂查询,JPA 自动生成的 SQL 可能不如手动编写的高效。
    • 优点:JPA 支持缓存、批量处理、懒加载等优化手段,适合处理较为复杂的实体关联和查询。
    • 缺点:对于复杂查询、批量操作,生成的 SQL 可能效率不高。
  • JDBC API:由于 JDBC 是直接操作数据库的 API,它的性能较为直接,没有任何额外的开销。开发人员可以对 SQL 进行全面优化,适合需要精细控制性能的场景。
    • 优点:提供最大限度的 SQL 操作控制,适合性能优化场景。
    • 缺点:需要手动编写 SQL,增加了开发复杂度。

对比:Spring Data JPA 的抽象层会带来一定的性能开销,特别是在处理复杂查询时。JDBC 则提供了对性能的最大控制,适合需要高性能和灵活 SQL 优化的场景。

5. 事务管理

  • Spring Data JPA:Spring Data JPA 可以轻松使用 Spring 的 @Transactional 注解来管理事务。由于 JPA 是基于 Hibernate 或其他 JPA 实现的 ORM 框架,事务管理相对简单,并且 JPA 支持对多个实体类的级联事务操作。
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
  • JDBC API:JDBC 也可以通过 @Transactional 进行事务管理,但需要手动处理复杂的事务逻辑,特别是涉及多个 SQL 操作时,开发人员必须确保所有的 SQL 语句在同一个事务中执行。
@Transactional
public void addUserWithTransaction(String name, String email) {
jdbcTemplate.update("INSERT INTO users (name, email) VALUES (?, ?)", name, email);
}

对比:两者都可以通过 Spring 的事务管理机制来处理事务,但 JPA 的级联事务管理更加方便,而 JDBC 需要开发者手动处理事务边界。

总结

特点 Spring Data JPA JDBC API
开发复杂度 高度抽象,减少 SQL 操作,适合快速开发 灵活,但需要手动编写 SQL,适合高级开发者
SQL 控制 SQL 由框架自动生成,减少了手动编写 SQL 完全由开发者控制 SQL 语句,灵活性高
性能 适合一般的 CRUD 和简单查询,复杂查询时可能有性能损耗 高效,适合性能要求高、需要精细控制的场景
事务管理 支持级联事务,使用简单 手动管理事务,适合复杂事务处理
复杂关联关系处理 提供 ORM 支持,方便处理实体间的关系 不支持 ORM,手动管理关联关系
使用场景 适合快速开发、面向对象的数据库访问 适合高性能、需要直接控制 SQL 的应用

对于多表级联查询的选择

在实际生产过程中,如学生选课系统中选课模块的sql语句

select choice.*  
,teacher.name as teacherName
,student.name as studentName
,course.location as courseLocation
,course.time as courseTime
from choice ,teacher ,student ,course
where choice.teacher_id=teacher.id
and choice.student_id=student.id
and choice.course_id=course.id

针对你需要实现的这种 多表关联查询 的 SQL 语句,选择使用 Spring Data JPA 还是 JDBC API 取决于你的需求:

SQL 描述:

你的 SQL 语句是一个典型的多表查询,涉及四张表:choiceteacherstudentcourse。你想要查询 choice 表,并获取相关联的 teacherstudentcourse 的一些字段信息。

对比考虑点:

  • Spring Data JPA 提供了对象关系映射 (ORM),你可以将这些表映射为 Java 对象(实体类),通过 JPA 的关联关系和 @Query 注解,或使用 JPQL 或 Criteria API 来实现类似的查询。这种方法可以自动处理对象和数据库之间的映射,简化代码管理。
  • JDBC API 需要手动编写原生 SQL 查询。它适合于你需要更精确的控制,或者不想创建大量实体类的场景,尤其是当查询逻辑较复杂时,使用 JDBC 直接编写 SQL 会更加灵活和高效。

使用 Spring Data JPA 实现

  1. 实体类关联映射
    你可以为 choiceteacherstudentcourse 定义实体类,并在实体类之间定义相应的关系映射,如 @ManyToOne@OneToMany 等。
    • Choice 实体类:
@Entity
public class Choice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
@JoinColumn(name = "teacher_id")
private Teacher teacher;

@ManyToOne
@JoinColumn(name = "student_id")
private Student student;

@ManyToOne
@JoinColumn(name = "course_id")
private Course course;
}
- 其他实体类同样定义:
@Entity
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}

@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String location;
private String time;
}
  1. 使用 JPQL 或者 @Query 实现查询你可以在 ChoiceRepository 中通过 JPQL 或原生 SQL 来实现查询:
@Query("SELECT c AS choice, t.name AS teacherName, s.name AS studentName, cr.location AS courseLocation, cr.time AS courseTime " +
"FROM Choice c " +
"JOIN c.teacher t " +
"JOIN c.student s " +
"JOIN c.course cr " +
"WHERE c.teacher.id = :teacherId AND c.student.id = :studentId")
List<Map<String, Object>> findCustomChoiceDetails(@Param("teacherId") Long teacherId, @Param("studentId") Long studentId);
  1. 自动映射查询结果在这种模式下,Spring Data JPA 会自动将结果映射为实体类,简化了 SQL 和 Java 对象之间的映射过程。如果你的查询需求变化较小,使用 Spring Data JPA 的优点是更清晰和维护方便。

使用 JDBC API 实现

  1. 直接编写原生 SQL 查询使用 JDBC API,你可以通过 JdbcTemplate 手动编写查询语句,并将结果映射为自定义对象或 Map<String, Object>。这种方式下,你不需要定义实体类和对象关系映射,直接编写 SQL,更灵活。
@Repository
public class ChoiceRepository {

@Autowired
private JdbcTemplate jdbcTemplate;

public List<Map<String, Object>> findCustomChoiceDetails() {
String sql = "SELECT choice.*, teacher.name AS teacherName, student.name AS studentName, " +
"course.location AS courseLocation, course.time AS courseTime " +
"FROM choice " +
"JOIN teacher ON choice.teacher_id = teacher.id " +
"JOIN student ON choice.student_id = student.id " +
"JOIN course ON choice.course_id = course.id";

return jdbcTemplate.queryForList(sql);
}
}
  1. 手动映射结果你可以使用 JdbcTemplate.queryForList() 返回一个 List<Map<String, Object>>,每个 Map 中包含查询结果的列和值。此方法灵活,不需要为每张表创建实体类,并且可以精确控制 SQL 查询。
  2. 结果映射为自定义对象(可选)如果你想将结果映射为自定义的 DTO(数据传输对象),可以通过 RowMapper 手动映射查询结果:
public List<ChoiceDTO> findCustomChoiceDetails() {
String sql = "SELECT choice.*, teacher.name AS teacherName, student.name AS studentName, " +
"course.location AS courseLocation, course.time AS courseTime " +
"FROM choice " +
"JOIN teacher ON choice.teacher_id = teacher.id " +
"JOIN student ON choice.student_id = student.id " +
"JOIN course ON choice.course_id = course.id";

return jdbcTemplate.query(sql, (rs, rowNum) ->
new ChoiceDTO(
rs.getLong("id"),
rs.getString("teacherName"),
rs.getString("studentName"),
rs.getString("courseLocation"),
rs.getString("courseTime"))
);
}

选择合适的技术:

  • 使用 Spring Data JPA:
    • 如果你希望简化代码,减少 SQL 操作,且项目中的数据表关系稳定,建议使用 Spring Data JPA。它的 ORM 功能会自动帮助你完成对象和表之间的映射,尤其适合处理关联关系的项目。
    • 但是,Spring Data JPA 在复杂查询中可能不够灵活,某些情况下自动生成的 SQL 性能可能不如手写的高效。
  • 使用 JDBC API:
    • 如果你需要完全控制 SQL 语句,或者项目中对性能要求高、查询语句比较复杂,建议使用 JDBC API。手动编写 SQL 能够确保查询的灵活性和高效性,适合对查询逻辑要求更高的场景。
    • 但需要注意,使用 JDBC API 需要更多的手动编码工作,特别是对结果集的处理。

总结:

  • 如果你追求简化代码和快速开发,Spring Data JPA 是一个好的选择,但对复杂的自定义查询需要更多的配置。
  • 如果你希望完全控制 SQL 语句和数据库交互,JDBC API 更灵活且适合高性能、复杂查询的场景。

根据你的查询需求,JDBC API 会为你提供更多的灵活性。你可以直接编写 SQL,方便处理多表连接和复杂查询逻辑。如果你更倾向于对象映射和简化开发,Spring Data JPA 也是一个不错的选择。

我个人推荐使用 JDBC API,但是要了解Spring Data JPA的使用方法