1. 概述
Spring Data JPA实现为Jakarta Persistence API提供Repository支持,用于管理持久化和对象关系映射和函数。
在本文中,我们将探索使用JPA计算表中行数的不同方法。
2. 实体类
对于我们的示例,我们将使用帐户实体,它与权限实体具有一对一的关系。
@Entity
@Table(name="ACCOUNTS")
public class Account {
@Id
@GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "accounts_seq")
@SequenceGenerator(name = "accounts_seq", sequenceName = "accounts_seq", allocationSize = 1)
@Column(name = "user_id")
private int userId;
private String username;
private String password;
private String email;
private Timestamp createdOn;
private Timestamp lastLogin;
@OneToOne
@JoinColumn(name = "permissions_id")
private Permission permission;
// getters , setters
}
权限属于账户实体。
@Entity
@Table(name="PERMISSIONS")
public class Permission {
@Id
@GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "permissions_id_sq")
@SequenceGenerator(name = "permissions_id_sq", sequenceName = "permissions_id_sq", allocationSize = 1)
private int id;
private String type;
// getters , setters
}
3. 使用JPA Repository
Spring Data JPA提供了一个可扩展的Repository接口,它提供了开箱即用的查询方法和派生查询方法,例如findAll()、findBy()、save()、saveAndFlush()、count()、countBy()、delete()、deleteAll()。
我们将定义扩展JpaRepository接口的AccountRepository接口,以便可以访问count方法。
如果我们需要根据一个或多个条件进行计数,例如countByFirstname()、countByPermission()或countByPermissionAndCreatedOnGreaterThan(),我们所需要的只是AccountRepository接口中方法的名称,然后查询派生将负责为其定义适当的SQL:
public interface AccountRepository extends JpaRepository<Account, Integer> {
long countByUsername(String username);
long countByPermission(Permission permission);
long countByPermissionAndCreatedOnGreaterThan(Permission permission, Timestamp ts)
}
在下面的示例中,我们将使用逻辑类中的AccountRepository来执行计数操作。
3.1 计算表中的所有行数
我们将定义一个逻辑类,在其中注入AccountRepository,对于简单的行count()操作,我们可以只使用accountRepository.count()即可得到结果。
@Service
public class AccountStatsLogic {
@Autowired
private AccountRepository accountRepository;
public long getAccountCount(){
return accountRepository.count();
}
}
3.2 根据单一条件计算结果行数
正如我们上面所定义的,AccountRepository包含countByPermission和countByUsername等方法名称,Spring Data JPA查询派生将派生这些方法的查询。
所以我们可以在逻辑类中使用这些方法进行条件计数,也会得到结果。
@Service
public class AccountStatsLogic {
@Autowired
private AccountRepository accountRepository;
@Autowired
private PermissionRepository permissionRepository;
public long getAccountCountByUsername(){
String username = "user2";
return accountRepository.countByUsername(username);
}
public long getAccountCountByPermission(){
Permission permission = permissionRepository.findByType("reader");
return accountRepository.countByPermission(permission);
}
}
3.3 根据多个条件统计结果行数
我们还可以在查询派生中包含多个条件,如下所示,其中包含Permission和CreatedOnGreaterThan。
@Service
public class AccountStatsLogic {
@Autowired
private AccountRepository accountRepository;
@Autowired
private PermissionRepository permissionRepository;
public long getAccountCountByPermissionAndCreatedOn() throws ParseException {
Permission permission = permissionRepository.findByType("reader");
Date parsedDate = getDate();
return accountRepository.countByPermissionAndCreatedOnGreaterThan(permission, new Timestamp(parsedDate.getTime()));
}
}
4. 使用CriteriaQuery
在JPA中计算行数的下一个方法是使用CriteriaQuery接口,这个接口允许我们以面向对象的方式编写查询,这样我们就可以跳过编写原始SQL查询的知识。
它要求我们构造一个CriteriaBuilder对象,然后它帮助我们构造CriteriaQuery。CriteriaQuery准备就绪后,我们可以使用entityManager中的createQuery方法来执行查询并获取结果。
4.1 计算所有行数
现在,当我们使用CriteriaQuery构造查询时,我们可以定义要计数的选择查询,如下所示。
public long getAccountsUsingCQ() throws ParseException {
// creating criteria builder and query
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
Root<Account> accountRoot = criteriaQuery.from(Account.class);
// select query
criteriaQuery.select(builder.count(accountRoot));
// execute and get the result
return entityManager.createQuery(criteriaQuery).getSingleResult();
}
4.2 根据单一条件统计结果行数
我们还可以扩展select查询以包含where条件,以根据某些条件过滤我们的查询。我们可以向构建器实例添加一个谓词并将其传递给where子句。
public long getAccountsByPermissionUsingCQ() throws ParseException {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
Root<Account> accountRoot = criteriaQuery.from(Account.class);
List<Predicate> predicateList = new ArrayList<>(); // list of predicates that will go in where clause
predicateList.add(builder.equal(accountRoot.get("permission"), permissionRepository.findByType("admin")));
criteriaQuery
.select(builder.count(accountRoot))
.where(builder.and(predicateList.toArray(new Predicate[0])));
return entityManager.createQuery(criteriaQuery).getSingleResult();
}
4.3 根据多个条件统计结果行数
在我们的谓词中,可以添加多个条件来过滤我们的查询,构建器实例提供了equal()和greaterThan()等条件方法来支持查询条件。
public long getAccountsByPermissionAndCreateOnUsingCQ() throws ParseException {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
Root<Account> accountRoot = criteriaQuery.from(Account.class);
List<Predicate> predicateList = new ArrayList<>();
predicateList.add(builder.equal(accountRoot.get("permission"), permissionRepository.findByType("reader")));
predicateList.add(builder.greaterThan(accountRoot.get("createdOn"), new Timestamp(getDate().getTime())));
criteriaQuery
.select(builder.count(accountRoot))
.where(builder.and(predicateList.toArray(new Predicate[0])));
return entityManager.createQuery(criteriaQuery).getSingleResult();
}
5. 使用JPQL查询
执行计数的下一个方法是使用JPQL。JPQL查询针对实体而不是直接针对数据库工作,或多或少看起来像SQL查询。我们总是可以编写一个JPQL查询来计算JPA中的行数。
5.1 计算所有行数
entityManager提供了一个createQuery()方法,该方法将JPQL查询作为参数并在数据库上执行该查询。
public long getAccountsByJPQL() throws ParseException {
Query query = entityManager.createQuery("SELECT COUNT(*) FROM Account a");
return (long) query.getSingleResult();
}
5.2 根据单一条件统计结果行数
在JPQL查询中,我们可以像在原始SQL中一样包含WHERE条件来过滤查询并计算返回的行数。
public long getAccountsByPermissionUsingJPQL() throws ParseException {
Query query = entityManager.createQuery("SELECT COUNT(*) FROM Account a WHERE a.permission = ?1");
query.setParameter(1, permissionRepository.findByType("admin"));
return (long) query.getSingleResult();
}
5.3 根据多个条件计算结果行数
在JPQL查询中,我们可以像在原始SQL中一样在WHERE子句中包含多个条件来过滤查询并计算返回的行数。
public long getAccountsByPermissionAndCreatedOnUsingJPQL() throws ParseException {
Query query = entityManager.createQuery("SELECT COUNT(*) FROM Account a WHERE a.permission = ?1 and a.createdOn > ?2");
query.setParameter(1, permissionRepository.findByType("admin"));
query.setParameter(2, new Timestamp(getDate().getTime()));
return (long) query.getSingleResult();
}
6. 使用Hibernate Criteria计算行数
对于使用旧版Hibernate的应用程序或仍使用Criteria API的情况,Hibernate提供了自己的Criteria接口来构建动态查询。在未使用JPA的CriteriaQuery API的情况下,这可以成为计算行数的有用替代方案。
尽管Hibernate的Criteria API已被弃用,取而代之的是CriteriaQuery API,但它在许多旧式应用程序中仍然很有用。
在此示例中,我们将使用两个实体Foo和Bar,它们通过多对一关系关联。每个Foo对象都链接到一个特定的Bar对象,我们将根据应用于Foo及其关联Bar的条件来计算Foo对象的数量。
6.1 统计表中的所有行
使用Hibernate Criteria API,我们可以通过在Criteria上设置Projection来计算表中的所有行。在此示例中,我们将配置Projections.rowCount()来检索Foo实体中的行数:
public long countAllRowsUsingHibernateCriteria() {
Session session = entityManager.unwrap(Session.class);
Criteria criteria = session.createCriteria(Foo.class);
criteria.setProjection(Projections.rowCount());
Long count = (Long) criteria.uniqueResult();
return count != null ? count : 0L;
}
在countAllRowsUsingHibernateCriteria()方法中,我们首先通过调用unwrap()方法从EntityManager获取Hibernate Session。然后,我们为实体创建一个Criteria实例,使用Projections.rowCount()指定我们想要计算所有行数。最后,我们调用uniqueResult()来检索查询中的单个计数结果。
6.2 根据单一条件计数行
要根据单个条件计算行数,我们可以向Criteria添加限制以过滤结果。在此示例中,我们根据特定的Bar对象名称计算Foo对象的数量:
public long getFooCountByBarNameUsingHibernateCriteria(String barName) {
Session session = entityManager.unwrap(Session.class);
Criteria criteria = session.createCriteria(Foo.class);
criteria.createAlias("bar", "b");
criteria.add(Restrictions.eq("b.name", barName));
criteria.setProjection(Projections.rowCount());
return (Long) criteria.uniqueResult();
}
在此代码中,我们使用createAlias()为bar字段创建别名“b”。然后,我们使用Restrictions.eq()添加限制,将其设置为按指定的barName过滤行。最后,我们使用Projections.rowCount()表示我们想要符合此条件的行数,并调用uniqueResult()来检索结果。
6.3 根据多个条件计算行数
我们可以向Criteria添加多个Restrictions,以便根据多个条件计算行数。在此示例中,我们计算Foo行数,其中关联的Bar的名称是特定值,并且Foo的名称与指定值匹配:
public long getFooCountByBarNameAndFooNameUsingHibernateCriteria(String barName, String fooName) {
Session session = entityManager.unwrap(Session.class);
Criteria criteria = session.createCriteria(Foo.class);
criteria.createAlias("bar", "b");
criteria.add(Restrictions.eq("b.name", barName));
criteria.add(Restrictions.eq("name", fooName));
criteria.setProjection(Projections.rowCount());
return (Long) criteria.uniqueResult();
}
与上一个示例类似,我们使用createAlias()为bar字段创建别名。这次,我们添加了两个限制:一个使用Restrictions.eq()按bar.name过滤行,另一个使用Restrictions.eq()过滤Foo对象名称与指定值匹配的行。然后,我们设置Projections.rowCount()来计算符合这些条件的行数,并调用uniqueResult()来检索结果。
7. 总结
在本教程中,我们学习了一些不同的方法来计算JPA中的行数,CriteriaBuilder和Spring Data JPA查询派生等规范帮助我们轻松编写不同条件的计数查询。
虽然CriteriaQuery和Spring Data JPA查询派生可以帮助我们构建不需要原始SQL知识的查询,但在某些用例中,如果它不能达到目的,我们始终可以使用JPQL编写原始SQL。
Post Directory
