当前位置:Java -> 使用 Spring JdbcTemplate 与 JdbcTemplateMapper
JdbcTemplate
是Spring使用JDBC从关系数据库中提供数据访问的选项。对于某些应用程序来说,使用ORM及其细微差别/复杂性可能不太合适,此时JdbcTemplate
就是个选择。
JdbcTemplate
抽象出了大量JDBC底层代码,但仍然十分冗长。当选择JdbcTemplate
时,通常开发人员会实现一个抽象层,以减少冗长性。
JdbcTemplateMapper
库是围绕JdbcTemplate
的一个包装器,其目标是减少JdbcTemplate
的使用冗长性。在模型上撒上几个注解,你就可以获得单行CRUD和流畅式的查询,例如hasOne
, hasMany
, hasMany through
(多对多)等,以及其他一些开发人员友好的特性。在适当的情况下使用它,并继续使用JdbcTemplate
进行其他功能,如存储过程、自定义查询、批处理等。
@Table(name = "employee")
public class Employee {
@Id(type = IdType.AUTO_INCREMENT)
private Integer id; // maps to id column in table. The id gets assigned on insert.
@Column
private String lastName; // maps to last_name column by default
@Column
private String firstName; // maps to first_name column by default
@Column
private LocalDateTime startDate; // maps to start_date column by default
@Column
private Integer departmentId; // maps to department_id. Foreign key
private Department department; // there are no mappings for relationships
...
}
@Table(name = "department")
public class Department {
@Id(type = IdType.AUTO_INCREMENT)
private Integer id; // maps to id column in table. The id gets assigned on insert
@Column(name = "department_name")
private String name; // maps to a non default column department_name in table
private List<Employee> employees = new ArrayList<>(); // there are no mappings for relationships
...
}
// Usage:
...
@Autowired
private JdbcTemplateMapper jtm;
...
Department dept = new Department();
dept.setName("HR department");
jtm.insert(dept); // auto assigns id on insert since id configured as auto increment
Employee emp = new Employee();
emp.setFirstName("John");
emp.setLastName("Doe");
emp.setStartDate(LocalDateTime.now());
emp.setDepartmentId(dept.getId());
jtm.insert(emp); // auto assigns id on insert since id configured as auto increment
emp = jtm.findById(Employee.class, emp.getId());
emp.setLastName("Smith");
jtm.update(emp);
...
JdbcTemplate
的冗长性体现在需要从多个表中查询信息时。比如,你想在两个表之间进行联接以填充关系。在这种情况下,你不能使用SELECT *
,因为涉及多个表,所以你必须在SELECT
语句中输入列名。在大多数情况下,仅此是不够的,因为来自多个表的列名可能存在冲突; 例如,id
和audit
字段等。这需要为列添加别名,以防止JDBC中出现冲突。在处理真实世界的应用程序时,表往往有相当多的列,这样查询变得非常冗长。请注意,这仅适用于SELECT
语句。您还需要实现自定义的RowMapper
,将值从ResultSet
映射到对象,其中您必须逐个映射每个ResultSet
列到对象。
与使用JdbcTemplateMapper
的Query
功能时下面的代码进行比较。它负责生成SQL语句。在幕后,它使用JdbcTemplate.query()
来检索记录。
// query the employee hasOne department relationship
List<Employee> employees = Query.type(Employee.class) // owning class
.hasOne(Department.class) // related class
.joinColumnOwningSide("department_id") // join column (the foreign key) is on owning (employee) table
.populateProperty("department")
.execute(jtm);
// query the department hasMany employee relationship
List<Department> departments =
Query.type(Department.class)
.hasMany(Employee.class)
.joinColumnManySide("department_id") // join column (the foreign key) is on many side table employee
.populateProperty("employees")
.where("department.department_name like ?", "HR%")
.orderBy("employee.last_name")
.execute(jtm);
Query
通过limitOffsetClause()
支持分页。要获得分页的总计数,可以使用QueryCount
。
QueryMerge
将一个查询的结果与另一个查询的结果合并。这在需要查询多个关系时非常方便。
以下是使用Query
、QueryMerge
和QueryCount
进行分页的简单示例。有关更详细的示例,请参阅JdbcTemplateMapper
文档。
// Paginated query for departments
List<Department> departments =
Query.type(Department.class)
.where("department_name like ?", "HR%")
.orderBy("department_name")
.limitOffsetClause("LIMIT 10 OFFSET 0") // MySQL syntax. Would be different for other databases.
.execute(jtm);
// QueryMerge will issue an SQL 'IN' clause with department ids and populate the employees
// for the corresponding departments
QueryMerge.type(Department.class)
.hasMany(Employee.class)
.joinColumnManySide("department_id") // join column (the foreign key) is on many side table employee
.populateProperty("employees")
.execute(jtm, departments); // merges employees to their corresponding department
// To get total count of records
Integer count = QueryCount.type(Department.class)
.where("department_name like ?", "HR%")
.execute(jtm);
对于一些应用程序,乐观锁定至关重要,这样就不会让更新发生冲突。该库通过@Version
注解提供了对乐观锁定的支持。在更新过期数据时,将抛出OptimisticLocking
异常。
<dependency>
<groupId>io.github.jdbctemplatemapper</groupId>
<artifactId>jdbctemplatemapper</artifactId>
<version>2.3.1</version> <!-- Check and use lastest from jdbctemplatemapper site -->
</dependency>
@Bean
public JdbcTemplateMapper jdbcTemplateMapper(JdbcTemplate jdbcTemplate) {
return new JdbcTemplateMapper(jdbcTemplate);
}
在application.properties文件中进行以下项配置,记录SQL。
# log the sql
logging.level.org.springframework.jdbc.core.JdbcTemplate=TRACE
# log the INSERT statements
logging.level.org.springframework.jdbc.core.simple.SimpleJdbcInsert=TRACE
# log the parameters of sql statement
logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE
该库提供了像@CreatedBy
、@UpdatedBy
、@CreatedOn
和@UpdatedOn
这样的记录审核注解,如果模型被这些注解标记,则会自动赋值。它支持Java8、Java11和Java17(所有LTSJava版本)以及相应的Spring Boot版本。例如,它适用于使用Java17的Spring Boot 3.x版本。该库的测试针对PostgreSQL、MySQL、Oracle和SQLServer运行。根据数据库/驱动程序的版本,它应该可以与其他关系数据库一起使用。
该库的唯一依赖是spring-boot-starter-jdbc
(这是Spring JdbcTemplate
所需的依赖项)。
这个教程的源代码在GitHub上可以找到。该教程针对MySQL数据库,并且源代码中提供了JdbcTemplate
的配置。还提供了运行代码的指导。
推荐阅读: FastApi、Flask、Django如何选择
本文链接: 使用 Spring JdbcTemplate 与 JdbcTemplateMapper