Spring Data JPA @Query注释示例

时间:2020-01-09 10:44:33  来源:igfitidea点击:

在Spring Data JPA Example中,我们已经看到了Spring Rest Controller + JPA(Hibernate)+ MySQL的集成示例。在该示例中,用户定义查询的查询查找策略是自动的,其中Spring框架通过解析方法名称来完成派生查询的工作。尽管从方法名称派生查询非常方便,但是我们可能会遇到这样的情况,即方法名称解析器不支持我们使用的关键字,否则方法名称将变得不必要的丑陋。在这种情况下,Spring Data JPA提供了两个选项

  • 我们可以使用@NamedQuery注释使用JPA命名查询。
  • 用@Query注释查询方法。

在本文中,我们将看到带有@Query注释的Spring数据JPA示例。

对于使用@NamedQuery注释的Spring Data JPA示例,请查看此文章Spring Data JPA @NamedQuery注释示例

带有@Query注释的Spring Data JPA

我们将使用Spring Web MVC创建一个Rest Web服务,使用的JPA实现是Hibernate,DB是MySQL。

该示例显示了@Query注释的用法。使用Spring Data JPA @Query注释的优点是,它在存储库接口中的查询方法上应用,而不是将其注释到域类中。这将从持久性特定的信息中释放域类,并将查询放置在存储库接口本身中。

请注意,使用@Query注释到查询方法的查询将优先于使用@NamedQuery定义的查询或者使用<named-query />元素以XML定义的命名查询。

Maven依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.theitroad</groupId>
  <artifactId>SpringJPAProject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>SpringJPA</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    <spring.version>5.1.8.RELEASE</spring.version>
    <spring.data>2.1.10.RELEASE</spring.data>
    <hibernate.jpa>5.4.3.Final</hibernate.jpa>
    <mysql.version>8.0.17</mysql.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!-- Spring data JPA -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>${spring.data}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.Hymanson.core</groupId>
      <artifactId>Hymanson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.0</version>
      <scope>provided</scope>
    </dependency>
    <!-- Hibernate -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${hibernate.jpa}</version>
    </dependency>
    <!-- MySQL Driver -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>
  </dependencies>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <release>11</release>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.1</version>
        <configuration>
          <warSourceDirectory>WebContent</warSourceDirectory>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

为Spring核心,Spring上下文以及Spring Web和Spring数据JPA添加了依赖项。

当使用Hibernate JPA实现时,添加了Hibernate的依赖关系。

MySQL连接器用于从Java应用程序连接到MySQL DB。

对于以JSON发送的Web服务响应,需要Hymanson数据绑定。

数据库表查询

可以使用以下查询创建用于此Spring数据JPA的MySQL DB表。

CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(45) DEFAULT NULL,
  `last_name` varchar(45) DEFAULT NULL,
  `department` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Spring数据JPA示例–实体类

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="employee")
public class Employee {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	@Column(name="first_name")
	private String firstName;
	@Column(name="last_name")
	private String lastName;
	@Column(name="department")
	private String dept;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getDept() {
		return dept;
	}
	public void setDept(String dept) {
		this.dept = dept;
	}

	@Override
	public String toString() {
		return "Id= " + getId() + " First Name= " + 
	           getFirstName() + " Last Name= " + getLastName() + 
	           " Dept= "+ getDept();
	}
}

这是映射到DB中的employee表的实体类。

@Entity注释指定此模型类为实体。
@Table注释指定实体的主表。
@Id注释指定实体的主键。
@GeneratedValue指定主键生成策略,在这种情况下,该策略是自动递增的。
@Column注释指定该字段的映射表列名称。

Spring Data JPA存储库

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import com.theitroad.springproject.model.Employee;

public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
	List<Employee> findByLastName(String lastName);
	
	@Query("select e from Employee e where e.dept = ?1")
	List<Employee> findByDepartment(String department);
}

EmployeeRepository接口扩展了CrudRepository,它使用域类来管理(在这种情况下为Employee),并且将域类的id类型作为类型参数。

除了从CrudRepository继承的方法外,EmployeeRepository接口还定义了两种方法
方法findByDepartment的查询带有使用@Query注释的方法注释,因此该查询用于该方法,而不是派生查询。
对于方法findByLastName,Spring数据使用方法名称派生查询。

Spring Data JPA示例–服务类

在服务层,我们将调用存储库方法。请注意,必须将存储库实例注入服务类中。

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.theitroad.springproject.dao.EmployeeRepository;
import com.theitroad.springproject.model.Employee;

@Service
public class EmployeeService {
	@Autowired
	private EmployeeRepository repository;
	
	public Employee getEmployeeById(int id) {
		return repository.findById(id).get();
	}
	
	public List<Employee> getAllEmployees(){
		return (List<Employee>) repository.findAll();
	}
	
	public void deleteEmployeeById(int id){
		repository.deleteById(id);
	}
	
	public Employee addEmployee(Employee emp) {
		return repository.save(emp);
	}
	
	public List<Employee> getEmployeeByLastName(String lastName) {
		return repository.findByLastName(lastName);
	}
	
	public List<Employee> getEmployeeByDepartment(String department) {
		return repository.findByDepartment(department);
	}
}

休息控制器

使用Rest控制器类,我们会将路径映射到要为请求调用的方法。

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.theitroad.springproject.model.Employee;
import com.theitroad.springproject.service.EmployeeService;

@RestController
@RequestMapping("/employee")
public class EmployeeController {
	@Autowired
	EmployeeService empService;
	@GetMapping("/{id}")
	public Employee getEmployeeById(@PathVariable int id) {
		return empService.getEmployeeById(id);
	}
	@GetMapping
	public List<Employee> getAllEmployees(){
		return empService.getAllEmployees();
	}
	@DeleteMapping("/{id}")
	@ResponseStatus(HttpStatus.OK)
	public void deleteEmployeeById(@PathVariable int id){
		empService.deleteEmployeeById(id);
	}
	@PostMapping
	@ResponseStatus(HttpStatus.CREATED)
	public Employee addEmployee(@RequestBody Employee emp) {
		return empService.addEmployee(emp);
	}
	@GetMapping("/lastname/{lastName}")
	public List<Employee> getEmployeeByLastName(@PathVariable String lastName) {
		return empService.getEmployeeByLastName(lastName);
	}
	@GetMapping("/dept/{department}")
	public List<Employee> getEmployeeByDepartment(@PathVariable String department) {
		return empService.getEmployeeByDepartment(department);
	}
}

Spring Data JPA –配置类

在此Spring数据JPA示例中,使用Java配置,因此使用@Configuration注释对类进行注释。

要设置DataSource DB属性是从属性文件中读取的,请使用@PropertySource注释配置属性文件的路径。

@EnableJpaRepositories注释启用JPA存储库。带有注释的值提供了用于扫描存储库的软件包。
@EnableTransactionManagement注释启用Spring的注释驱动的事务管理功能。

在此Java配置类中,我们设置了EntityManagerFactory并将Hibernate用作持久性提供程序。

import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableJpaRepositories(basePackages = "com.theitroad.springproject.dao", queryLookupStrategy=Key.CREATE_IF_NOT_FOUND)
@EnableTransactionManagement
@PropertySource("classpath:config/db.properties")
public class JPAConfig {
  @Autowired
  private Environment env;
  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.theitroad.springproject.model");
    factory.setDataSource(dataSource());
    factory.setJpaProperties(hibernateProperties());
    return factory;
  }

  @Bean
  public DataSource dataSource() {
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName(env.getProperty("db.driverClassName"));
    ds.setUrl(env.getProperty("db.url"));
    ds.setUsername(env.getProperty("db.username"));
    ds.setPassword(env.getProperty("db.password"));
    return ds;
  }

  Properties hibernateProperties() {
    Properties properties = new Properties();
    properties.setProperty("hibernate.dialect", env.getProperty("hibernate.sqldialect"));
    properties.setProperty("hibernate.show_sql", env.getProperty("hibernate.showsql"));
    return properties;
  }

  @Bean
  public PlatformTransactionManager transactionManager() {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory().getObject());
    return txManager;
  }
}

如果我们使用的是XML配置,则用于启用JPA存储库的配置为

<jpa:repositories base-package="com.theitroad.springproject.dao"/>

db.properties文件

db.driverClassName=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/theitroad
db.username=
db.password=
hibernate.sqldialect=org.hibernate.dialect.MySQLDialect
hibernate.showsql=true

要使用Java config而不是web.xml设置网络应用程序,我们需要以下类。

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfigInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }
}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.theitroad.springproject")
public class WebConfig implements WebMvcConfigurer{

}

部署Spring Data JPA应用程序

右键单击项目,然后选择"运行方式– Maven构建",提供目标为全新安装。如果构建成功,则将应用程序打包为war,可以将其部署在像Tomcat这样的Web容器上,然后测试该应用程序。