1.简介
在本文中,我们将演示如何利用最流行的ORM(对象关系映射)工具之一的Hibernate的功能 ,该工具可将面向对象的域模型转换为传统的关系数据库。 Hibernate是目前最流行的Java框架之一。 由于这个原因,我们在Java Code Geeks上提供了很多教程,其中大多数可以在此处找到。
在本课中,我们将创建一个基于Spring Boot的简单应用程序,它将利用Hibernate配置和Spring Data JPA的功能。 我们将使用H2内存数据库。 数据库的选择不应影响我们将构建的Spring Data定义,因为这是Hibernate和Spring Data JPA提供的主要优点。 它使我们能够将数据库查询与应用程序逻辑完全分开。
2.制作Spring Boot项目
在本课中,我们将使用最流行的Web工具之一来制作示例项目,并且不会从命令行执行它,而是使用Spring Initializr 。 只需在浏览器中打开链接并进行浏览即可。 要设置我们的项目,我们使用以下配置:
我们在此工具中添加了三个依赖项:
- Web :这是一个基本的Spring依赖关系,它将与配置有关的注释和基本注释收集到项目中。
- H2 :由于我们使用内存数据库,因此需要这种依赖性。
- Data JPA :我们将在数据访问层中使用Spring Data JPA。
接下来,解压缩下载的zip项目并将其导入您喜欢的IDE。
3. Maven依赖
首先,我们需要查看该工具将哪些Maven依赖项添加到了我们的项目中,以及需要哪些其他依赖项。 我们将对pom.xml文件具有以下依赖性:
pom.xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-search-orm</artifactId><version>5.6.1.Final</version>
</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId>
</dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency></dependencies>
在此处找到与Spring相关的最新依赖项。 我们添加了也需要执行Hibernate搜索的依赖项。
请注意,我们还在此处添加了H2数据库依赖关系及其范围(作为运行时),因为一旦应用程序停止运行,H2数据就会被冲走。 在本课程中,我们将不关注H2的实际工作原理,而将自己局限于Hibernate配置。 您可能还会看到我们如何使用Spring应用程序配置嵌入式H2控制台 。
最后,要了解添加此依赖项时添加到项目中的所有JAR,我们可以运行一个简单的Maven命令,当我们向其添加一些依赖项时,该命令使我们能够查看项目的完整依赖关系树。 这是我们可以使用的命令:
检查依赖树
mvn dependency:tree
当我们运行此命令时,它将向我们显示以下依赖关系树:
依赖树
[INFO] --------< com.javacodegeeks.example:JCG-BootHibernate-Example >---------
[INFO] Building JCG-BootHibernate-Example 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ JCG-BootHibernate-Example ---
[INFO] com.javacodegeeks.example:JCG-BootHibernate-Example:jar:1.0-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:1.5.6.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:1.5.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:1.5.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.6.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.1.11:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.1.11:compile
[INFO] | | | +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | | | \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-aop:jar:1.5.6.RELEASE:compile
[INFO] | | +- org.springframework:spring-aop:jar:4.3.10.RELEASE:compile
[INFO] | | \- org.aspectj:aspectjweaver:jar:1.8.10:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:1.5.6.RELEASE:compile
[INFO] | | +- org.apache.tomcat:tomcat-jdbc:jar:8.5.16:compile
[INFO] | | | \- org.apache.tomcat:tomcat-juli:jar:8.5.16:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:4.3.10.RELEASE:compile
[INFO] | +- org.hibernate:hibernate-core:jar:5.0.12.Final:compile
[INFO] | | +- antlr:antlr:jar:2.7.7:compile
[INFO] | | \- org.jboss:jandex:jar:2.0.0.Final:compile
[INFO] | +- javax.transaction:javax.transaction-api:jar:1.2:compile
[INFO] | +- org.springframework.data:spring-data-jpa:jar:1.11.6.RELEASE:compile
[INFO] | | +- org.springframework.data:spring-data-commons:jar:1.13.6.RELEASE:compile
[INFO] | | +- org.springframework:spring-orm:jar:4.3.10.RELEASE:compile
[INFO] | | +- org.springframework:spring-context:jar:4.3.10.RELEASE:compile
[INFO] | | +- org.springframework:spring-tx:jar:4.3.10.RELEASE:compile
[INFO] | | +- org.springframework:spring-beans:jar:4.3.10.RELEASE:compile
[INFO] | | +- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] | | \- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile
[INFO] | \- org.springframework:spring-aspects:jar:4.3.10.RELEASE:compile
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:1.5.6.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.5.6.RELEASE:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.16:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.16:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.16:compile
[INFO] | +- org.hibernate:hibernate-validator:jar:5.3.5.Final:compile
[INFO] | | +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] | | \- com.fasterxml:classmate:jar:1.3.3:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.9:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile
[INFO] | | \- com.fasterxml.jackson.core:jackson-core:jar:2.8.9:compile
[INFO] | +- org.springframework:spring-web:jar:4.3.10.RELEASE:compile
[INFO] | \- org.springframework:spring-webmvc:jar:4.3.10.RELEASE:compile
[INFO] | \- org.springframework:spring-expression:jar:4.3.10.RELEASE:compile
[INFO] +- org.hibernate:hibernate-search-orm:jar:5.6.1.Final:compile
[INFO] | \- org.hibernate:hibernate-search-engine:jar:5.6.1.Final:compile
[INFO] | +- org.apache.lucene:lucene-core:jar:5.5.4:compile
[INFO] | +- org.apache.lucene:lucene-misc:jar:5.5.4:compile
[INFO] | +- org.apache.lucene:lucene-analyzers-common:jar:5.5.4:compile
[INFO] | \- org.apache.lucene:lucene-facet:jar:5.5.4:compile
[INFO] | \- org.apache.lucene:lucene-queries:jar:5.5.4:compile
[INFO] +- org.hibernate:hibernate-entitymanager:jar:5.0.12.Final:compile
[INFO] | +- org.jboss.logging:jboss-logging:jar:3.3.1.Final:compile
[INFO] | +- dom4j:dom4j:jar:1.6.1:compile
[INFO] | +- org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final:compile
[INFO] | +- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final:compile
[INFO] | +- org.javassist:javassist:jar:3.21.0-GA:compile
[INFO] | \- org.apache.geronimo.specs:geronimo-jta_1.1_spec:jar:1.1.1:compile
[INFO] +- com.h2database:h2:jar:1.4.196:runtime
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:1.5.6.RELEASE:test
[INFO] +- org.springframework.boot:spring-boot-test:jar:1.5.6.RELEASE:test
[INFO] +- org.springframework.boot:spring-boot-test-autoconfigure:jar:1.5.6.RELEASE:test
[INFO] +- com.jayway.jsonpath:json-path:jar:2.2.0:test
[INFO] | \- net.minidev:json-smart:jar:2.2.1:test
[INFO] | \- net.minidev:accessors-smart:jar:1.1:test
[INFO] | \- org.ow2.asm:asm:jar:5.0.3:test
[INFO] +- junit:junit:jar:4.12:test
[INFO] +- org.assertj:assertj-core:jar:2.6.0:test
[INFO] +- org.mockito:mockito-core:jar:1.10.19:test
[INFO] | \- org.objenesis:objenesis:jar:2.1:test
[INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] +- org.skyscreamer:jsonassert:jar:1.4.0:test
[INFO] | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] +- org.springframework:spring-core:jar:4.3.10.RELEASE:compile
[INFO] \- org.springframework:spring-test:jar:4.3.10.RELEASE:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
注意到了什么? 通过向项目中添加四个依赖项,添加了如此多的依赖项。 Spring Boot本身会收集所有相关的依赖项,在这件事上对我们没有任何帮助。 最大的优点是,所有这些依赖项都可以保证彼此兼容。
4.项目结构
在继续进行并开始处理项目代码之前,让我们在这里介绍完成向项目添加所有代码后将拥有的项目结构:
我们将项目分为多个包,以便遵循关注点分离的原则,并且代码保持模块化。
请注意, indexpath目录是由Hibernate创建的,用于存储索引(在本课程的后面部分讨论),当您在IDE中导入项目时,该目录将不存在。
5.定义休眠方言
在application.properties文件中,我们定义了项目类路径上存在的Spring Data JPA使用的两个属性。 Spring Boot使用Hibernate作为默认的JPA实现。
application.properties
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect# Hibernate DDL auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.ddl-auto
属性在这里很重要。 因此,Spring Data将根据我们在项目中定义的实体自动创建数据库表,而列将由实体的字段构成。 正如我们将属性设置为update
,每当我们更新Entity类中的字段时,只要重新启动项目,该字段就会在数据库中自动更新。
6.定义实体
我们将在我们的项目中添加一个非常简单的模型Person
。 它的定义将非常标准,例如:
人.java
@Entity
@Indexed
public class Person {@Id@GeneratedValueprivate Long id;@Field(termVector = TermVector.YES)private String name;@Fieldprivate int age;// standard getters and setters@Overridepublic String toString() {return String.format("Person{id=%d, name='%s', age=%d}", id, name, age);}
}
为了简洁起见,我们省略了标准的getter和setter方法,但是由于Jackson在对象的序列化和反序列化过程中使用它们,因此必须将它们制成。
@Entity
注释将此POJO标记为将由Spring Data API管理的对象,并且其字段将被视为表列(除非标记为瞬态),而@Field
注释将此字段标记为Hibernate索引,以便我们可以在这些字段上也运行全文搜索查询。
最后,我们为toString()
方法添加了一个自定义实现,以便在测试应用程序时可以打印相关数据。
7.制作服务接口
在本节中,我们将定义一个服务接口,该接口将充当实现的合同,并代表我们的服务必须支持的所有操作。 这些动作将与结交新用户以及获取与数据库中对象有关的信息有关。
这是我们将使用的合同定义:
PersonService.java
public interface PersonService {Person createPerson(Person person);Person getPerson(Long id);Person editPerson(Person person);void deletePerson(Person person);void deletePerson(Long id);List<Person> getAllPersons(int pageNumber, int pageSize);List<Person> getAllPersons();long countPersons();List<Person> fuzzySearchPerson(String term);List<Person> wildCardSearchPerson(String term);
}
请注意,合同最后还包括两种方法,它们也为Hibernate搜索提供支持。
8.实施服务
我们将使用上面的接口定义来提供其实现,以便我们可以执行与我们先前定义的Person
实体相关的CRUD操作。 我们将在这里执行:
PersonServiceImpl.java
@Service
public class PersonServiceImpl implements PersonService {private final PersonRepository personRepository;private final PersonDAL personDAL;@Autowiredpublic PersonServiceImpl(PersonRepository personRepository, PersonDAL personDAL) {this.personRepository = personRepository;this.personDAL = personDAL;}@Overridepublic Person createPerson(Person person) {return personRepository.save(person);}@Overridepublic Person getPerson(Long id) {return personRepository.findOne(id);}@Overridepublic Person editPerson(Person person) {return personRepository.save(person);}@Overridepublic void deletePerson(Person person) {personRepository.delete(person);}@Overridepublic void deletePerson(Long id) {personRepository.delete(id);}@Overridepublic List<Person> getAllPersons(int pageNumber, int pageSize) {return personRepository.findAll(new PageRequest(pageNumber, pageSize)).getContent();}@Overridepublic List<Person> getAllPersons() {return personRepository.findAll();}@Overridepublic long countPersons() {return personRepository.count();}@Override@Transactional(readOnly = true)public List<Person> fuzzySearchPerson(String term) {return personDAL.fuzzySearchPerson(term);}@Override@Transactional(readOnly = true)public List<Person> wildCardSearchPerson(String term) {return personDAL.wildCardSearchPerson(term);}
}
我们只是使用DAL bean访问我们上面定义的方法。 我们还使用了@Transactional(readOnly = true)
批注,这样我们就不必在执行一些写操作时就需要打开一个Hibernate会话,但是由于我们只需要执行搜索,因此我们可以放心地提到readOnly
属性设置为true
。
9.定义JPA存储库
由于大多数操作都是由JPA Repository本身完成的,因此我们在这里定义它:
PersonRepository.java
@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}
尽管上面的接口定义是空的,但我们仍然需要了解一些要点:
-
@Repository
批注将此接口标记为Spring Bean,该Bean在应用程序启动时初始化。 有了这个注释,Spring可以很好地管理异常数据库的交互抛出 - 我们使用
Person
作为参数来表示此JPA接口将管理Person实体 - 最后,我们还传递了数据类型
Long
作为参数。 这表示Person
实体包含唯一标识符,其类型为Long。
10.定义数据访问层(DAL)接口
尽管我们已经定义了执行所有CRUD操作的JPA信息库,但我们仍将在DAL层中定义用于Hibernate自由文本搜索的查询。 让我们看一下我们定义的合同:
PersonDAL.java
public interface PersonDAL {List<Person> fuzzySearchPerson(String term);List<Person> wildCardSearchPerson(String term);
}
11.实现DAL接口
在我们定义的DAL接口中,我们将实现两种不同类型的自由文本搜索:
- 模糊搜索:当我们要查找与搜索词相距一定距离的词时,可以使用模糊搜索。 为了理解差距,让我们考虑一个例子。 术语
Hibernate
和Hibernat
有1由于缺少差距e
在后文中,术语Hibernate
和Hibernawe
还具有以下在后面的字符串即单个字符1的缺口w
可以更换成原字符串。 - 通配符搜索:这些就像具有匹配短语的SQL语句一样。 像
Hibernate
匹配短语一样,可以是Hiber
,bernate
等。
让我们在DAL层中实现这两个功能。
11.1定义模糊查询
我们将从模糊搜索实现开始。 这是一个非常智能和复杂的搜索,因为这需要对数据库索引中保存的每个术语进行标记化。 在此处阅读有关Lucene如何执行此操作的更多信息。
让我们在这里实现此搜索查询:
模糊查询
@PersistenceContext
private EntityManager entityManager;@Override
public List<Person> fuzzySearchPerson(String term) {FullTextEntityManager fullTextEntityManager =org.hibernate.search.jpa.Search.getFullTextEntityManager(entityManager);QueryBuilder queryBuilder =fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Person.class).get();Query fuzzyQuery = queryBuilder.keyword().fuzzy().withEditDistanceUpTo(2).withPrefixLength(0).onField("name").matching(term).createQuery();FullTextQuery jpaQuery =fullTextEntityManager.createFullTextQuery(fuzzyQuery, Person.class);return jpaQuery.getResultList();
}
我们仅使用2的编辑距离。这是Hibernate和Lucene引擎支持的最大间隙。
11.2定义通配符查询
通配符查询易于理解和实现。 就像SQL LIKE语句一样工作:
通配符查询
@Overridepublic List<Person> wildCardSearchPerson(String term) {FullTextEntityManager fullTextEntityManager =org.hibernate.search.jpa.Search.getFullTextEntityManager(entityManager);QueryBuilder queryBuilder =fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Person.class).get();Query wildcardQuery = queryBuilder.keyword().wildcard().onField("name").matching("*" + term + "*").createQuery();FullTextQuery jpaQuery =fullTextEntityManager.createFullTextQuery(wildcardQuery, Person.class);return jpaQuery.getResultList();}
我们在术语的前后使用*
,以便LIKE可以在两个方向上使用。
12.在Hibernate中建立搜索索引
在Hibernate开始存储索引数据之前,我们需要确保搜索索引确实存在。 可以通过在应用程序启动后立即构造它来完成:
BuildSearchIndex.java
@Component
public class BuildSearchIndex implements ApplicationListener<ApplicationReadyEvent> {@PersistenceContextprivate EntityManager entityManager;@Overridepublic void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {try {FullTextEntityManager fullTextEntityManager =Search.getFullTextEntityManager(entityManager);fullTextEntityManager.createIndexer().startAndWait();}catch (InterruptedException e) {System.out.println("An error occurred trying to build the serach index: " +e.toString());}return;}
}
尽管我们在Hibernate中构造了一个搜索索引,但是索引数据将存储在哪里。 接下来,我们将对其进行配置。
13.存储索引数据
由于Hibernate需要存储Index数据,以便不必在每次执行操作时都重建索引数据,因此我们将为Hibernate提供文件系统目录,以在其中存储此数据:
application.properties
# Specify the Lucene Directory
spring.jpa.properties.hibernate.search.default.directory_provider = filesystem# Using the filesystem DirectoryProvider you also have to specify the default
# base directory for all indexes
spring.jpa.properties.hibernate.search.default.indexBase = indexpath
directory_provider
仅提供哪种类型的系统将存储数据,因为我们甚至可以将索引数据存储到云中。
14.创建命令行运行器
现在,我们准备运行我们的项目。 将样本数据插入
DataJpaApp.java
@SpringBootApplication
public class DataJpaApp implements CommandLineRunner {private static final Logger LOG = LoggerFactory.getLogger("JCG");@Autowiredprivate PersonService service;public static void main(String[] args) {SpringApplication.run(DataJpaApp.class, args);}@Overridepublic void run(String... strings) {LOG.info("Current objects in DB: {}", service.countPersons());Person person = service.createPerson(new Person("Shubham", 23));LOG.info("Person created in DB : {}", person);LOG.info("Current objects in DB: {}", service.countPersons());List<Person> fuzzySearchedPersons = service.fuzzySearchPerson("Shubha");LOG.info("Founds objects in fuzzy search: {}", fuzzySearchedPersons.get(0));List<Person> wildSearchedPersons = service.wildCardSearchPerson("hub");LOG.info("Founds objects in wildcard search: {}", wildSearchedPersons.get(0));person.setName("Programmer");Person editedPerson = service.editPerson(person);LOG.info("Person edited in DB : {}", person);service.deletePerson(person);LOG.info("After deletion, count: {}", service.countPersons());}
}
15.使用Maven运行项目
使用maven可以轻松运行应用程序,只需使用以下命令:
运行应用程序
mvn spring-boot:run
一旦运行项目,我们将看到以下输出:
如预期的那样,我们首先创建了一些样本数据,并通过调用count()
方法调用对其进行了确认。 最后,调用搜索方法以获得预期结果。
16.结论
在本课程中,我们研究了如何使用Hibernate配置Spring Data API,以及如何仅通过为实体定义POJO类就可以帮助我们在数据库中自动构造Tables。 即使更新实体,也不必担心在数据库中进行更改!
我们还使用Hibernate搜索运行了一些示例,这些示例由Lucene引擎本身提供了用于索引的功能,而Hibernate为我们提供了有关Lucene功能的有用包装。
17.下载源代码
这是Spring Boot和Hibernate ORM Framework的一个示例。
您可以在此处下载此示例的完整源代码: JCG-BootHibernate-Example
翻译自: https://www.javacodegeeks.com/2018/04/spring-hibernate-tutorial.html