在项目开发过程中,使用Springboot集成HQL,在此用于记录,下方有原生方法及HQL注意事项和示例,有兴趣的可往下阅读。
// 以下是简化的示例代码,实际的实现会更复杂,涉及更多的配置解析和资源初始化
public class HibernatePersistence implements PersistenceProvider {
@Override
public EntityManagerFactory createEntityManagerFactory(String emName, Map properties) {
EJB3Configuration configuration = new EJB3Configuration();
// 解析 persistence.xml 并配置相关信息
// 配置可能包括数据源、实体类、映射信息等
configuration.configure(emName, properties);
return configuration.buildEntityManagerFactory();
}
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
EJB3Configuration configuration = new EJB3Configuration();
// 根据容器提供的信息进行配置
configuration.configure(info, properties);
return configuration.buildEntityManagerFactory();
}
}
public class EntityManagerFactoryImpl implements EntityManagerFactory {
private SessionFactory sessionFactory;
@Override
public EntityManager createEntityManager() {
return new EntityManagerImpl(sessionFactory);
}
}
public class EntityManagerImpl extends AbstractEntityManagerImpl implements EntityManager {
@Override
public Session getSession() {
// 获取 Hibernate 的 Session 对象的具体实现逻辑
return sessionFactory.getCurrentSession();
}
// 实现 JPA 的 EntityManager 接口的各种方法,如 persist 方法
@Override
public void persist(Object entity) {
Session session = getSession();
session.save(entity);
}
}
public class QueryImpl implements Query {
private EntityManagerImpl entityManagerImpl;
public QueryImpl(EntityManagerImpl entityManagerImpl) {
this.entityManagerImpl = entityManagerImpl;
}
@Override
public List getResultList() {
Session session = entityManagerImpl.getSession();
// 执行 Hibernate 的查询操作,将结果转换为符合 JPA 的结果集
return session.createQuery(...).list();
}
}
public class TransactionImpl implements EntityTransaction {
private EntityManagerImpl entityManagerImpl;
public TransactionImpl(EntityManagerImpl entityManagerImpl) {
this.entityManagerImpl = entityManagerImpl;
}
@Override
public void commit() {
Session session = entityManagerImpl.getSession();
session.getTransaction().commit();
}
}
EntityManager:
是 JPA 的核心接口,用于管理持久化单元和实现与数据库的交互,提供对数据库的基本操作,如保存、更新、删除和查询实体对象。
可通过 EntityManagerFactory 创建,每个持久化单元通常对应一个 EntityManager 实例。在 Spring 中,可通过注入 EntityManager 并配合 @PersistenceContext 注解使用。
常用方法:
persist(entity):将实体对象保存到数据库,执行 INSERT 操作。
@PersistenceContext
private EntityManager entityManager;
public void saveEntity(Entity entity) {
entityManager.persist(entity);
}
merge(entity):将实体对象更新到数据库,执行 UPDATE 操作。
@PersistenceContext
private EntityManager entityManager;
public void updateEntity(Entity entity) {
entityManager.merge(entity);
}
remove(entity):从数据库中删除实体对象,执行 DELETE 操作。
@PersistenceContext
private EntityManager entityManager;
public void deleteEntity(Entity entity) {
entityManager.remove(entity);
}
find(entityClass, primaryKey):根据主键查询实体对象。
@PersistenceContext
private EntityManager entityManager;
public Entity findEntityById(Long id) {
return entityManager.find(Entity.class, id);
}
JpaRepository:
是 Spring Data JPA 提供的接口,是 PagingAndSortingRepository 和 QueryByExampleExecutor 的子接口,继承了两者的方法并提供更多 JPA 相关方法。
封装了通用的数据访问操作,提供更高级别的抽象,开发者无需编写具体的数据访问代码。是一个泛型接口,将实体类和主键类型作为泛型参数传递给它可实现对实体对象的 CRUD 操作。在 Spring 中,可通过继承 JpaRepository 接口自定义数据访问接口,Spring Data JPA 会自动生成实现。
常用方法:
// 定义一个继承自 JpaRepository 的数据访问接口
public interface EntityRepository extends JpaRepository<Entity, Long> {
// 可直接使用 JpaRepository 提供的方法,无需编写具体实现
}
自定义方法:
// 定义一个继承自 JpaRepository 的数据访问接口
public interface EntityRepository extends JpaRepository<Entity, Long> {
// 根据属性查询实体对象列表
List<Entity> findByProperty(String property);
// 根据属性查询实体对象并分页
Page<Entity> findByProperty(String property, Pageable pageable);
// 根据属性查询实体对象并排序
List<Entity> findByProperty(String property, Sort sort);
// 自定义查询语句
@Query("SELECT e FROM Entity e WHERE e.property = :property")
List<Entity> customQuery(@Param("property") String property);
}
在 Service 或 Repository 类中使用 @PersistenceContext
注解注入 EntityManager。
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
@Repository
public class MyRepository {
@PersistenceContext
private EntityManager entityManager;
}
HQL 是面向对象的查询语言,使用实体类名而非表名,支持使用别名简化查询。
String hql = "SELECT e FROM Entity e WHERE e.property = :property";
通过调用 createQuery ()
方法创建查询对象。
TypedQuery<Entity> query = entityManager.createQuery(hql, Entity.class);
使用 setParameter ()
方法设置参数的值。
query.setParameter("property", "someValue");
使用 getResultList ()
获取结果列表。
List<Entity> entities = query.getResultList();
使用 getSingleResult () 获取单个结果,注意处理 NoResultException。
try {
Entity entity = query.getSingleResult();
// 处理查询结果
} catch (NoResultException e) {
// 处理查询结果为空的情况
}
七、项目中的使用步骤
添加依赖:在 pom.xml
文件中添加 Spring Boot Starter Data JPA 依赖,它会自动引入 Hibernate 作为 JPA 的实现。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
配置数据源:在 application.yml
或 application.properties
中配置数据源信息,例如:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springbootdata
username: root
password: mysql
配置 JPA 和 Hibernate 属性:在 application.yml
或 application.properties
中配置 JPA 和 Hibernate 的相关属性,例如:
spring:
jpa:
show-sql: true
hibernate:
ddl-auto: create
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
format_sql: true
use_sql_comments: true
创建实体类:通过注解方式定义表结构,例如:
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
@Table(name = "users")
public class Account {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@Id
int id;
@Column(name = "username")
String username;
@Column(name = "password")
String password;
}
创建 Repository 接口:使用 Spring Data JPA 的 Repository 接口来简化数据访问层的开发。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
// 可以在此添加自定义的查询方法
}
使用 Repository 接口:在 Service 层中注入并使用 Repository 接口。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountService {
@Autowired
private AccountRepository accountRepository;
public List<Account> findAllAccounts() {
return accountRepository.findAll();
}
}
select
的查询:HQL查询一般需要使用 select
来明确查询的字段。但是,如果你不加 select
,HQL会默认查询所有字段,类似于SQL中的 SELECT * FROM
。不过,通常不推荐使用这种写法,因为这样返回的结果可能会导致内存开销过大,尤其在查询的字段较多时。
示例:
String hql = "from Employee"; // 不加 select,默认查询所有字段
Query query = session.createQuery(hql);
List employees = query.list();
注意: 这种查询会返回整个 Employee
实体类的列表。
如果你只想查询一个或者多个特定的字段,可以通过在 select
后面指定字段。这样HQL会返回一个包含指定字段的结果集。返回的结果通常是一个Object数组或者是指定类型的对象。
示例:
String hql = "select e.name, e.salary from Employee e"; // 返回指定字段
Query query = session.createQuery(hql);
List
这里返回的是 Employee
实体的 name
和 salary
字段,结果是一个 Object[]
数组。
如果你只需要查询一个字段,返回的是单个值,可以直接返回该字段。你可以直接指定单一字段,这样返回的结果就是一个单一的值,而不是一个实体对象。
示例:
String hql = "select e.salary from Employee e"; // 返回单个字段
Query query = session.createQuery(hql);
List salaries = query.list(); // 返回 salary 字段的列表
for (Double salary : salaries) {
System.out.println(salary);
}
这里返回的是 salary
字段的列表,类型为 Double
。
聚合函数是HQL中常用的功能,可以通过 count()
, sum()
, avg()
, min()
, max()
等函数来进行统计查询。
示例:
// 计算平均工资
String hql = "select avg(e.salary) from Employee e";
Query query = session.createQuery(hql);
Double averageSalary = (Double) query.uniqueResult(); // 返回一个单一的结果
System.out.println("Average Salary: " + averageSalary);
示例 2: 使用 count()
统计员工人数:
// 统计员工数量
String hql = "select count(e) from Employee e";
Query query = session.createQuery(hql);
Long employeeCount = (Long) query.uniqueResult();
System.out.println("Employee Count: " + employeeCount);
示例 3: 使用 sum()
计算所有员工的总工资:
// 计算总工资
String hql = "select sum(e.salary) from Employee e";
Query query = session.createQuery(hql);
Double totalSalary = (Double) query.uniqueResult();
System.out.println("Total Salary: " + totalSalary);
group by
和 having
:HQL还支持 group by
和 having
子句,可以用于分组查询。
示例:
// 按部门分组,计算每个部门的员工人数
String hql = "select e.department, count(e) from Employee e group by e.department";
Query query = session.createQuery(hql);
List
HQL直接返回Map类型
示例:
String hql = "select NEW MAP(s.id as sid, s.name as sname) from section s"
Query query = session.createQuery(hql);
List
直接返回 Map
可能会遇到以下几种问题:
使用 NEW MAP
返回 Map
类型的查询结果时,最好的做法是:
List
封装每一条记录。select
:默认查询所有字段。select
指定要查询的字段,可以返回一个包含多个字段的 Object[]
数组。count()
, sum()
, avg()
等。group by
和 having
:用于对查询结果进行分组。Map
类型,最好外面再用一层 List
包含,确保每个 Map
对应一行数据。