Spring休眠教程

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 。 只需在浏览器中打开链接并进行浏览即可。 要设置我们的项目,我们使用以下配置:

Spring Initializr配置

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
    @GeneratedValue
    private Long id;

    @Field(termVector = TermVector.YES)
    private String name;

    @Field
    private int age;

    // standard getters and setters

    @Override
    public 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;

    @Autowired
    public PersonServiceImpl(PersonRepository personRepository, PersonDAL personDAL) {
        this.personRepository = personRepository;
        this.personDAL = personDAL;
    }

    @Override
    public Person createPerson(Person person) {
        return personRepository.save(person);
    }

    @Override
    public Person getPerson(Long id) {
        return personRepository.findOne(id);
    }

    @Override
    public Person editPerson(Person person) {
        return personRepository.save(person);
    }

    @Override
    public void deletePerson(Person person) {
        personRepository.delete(person);
    }

    @Override
    public void deletePerson(Long id) {
        personRepository.delete(id);
    }

    @Override
    public List<Person> getAllPersons(int pageNumber, int pageSize) {
        return personRepository.findAll(new PageRequest(pageNumber, pageSize)).getContent();
    }

    @Override
    public List<Person> getAllPersons() {
        return personRepository.findAll();
    }

    @Override
    public 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接口中,我们将实现两种不同类型的自由文本搜索:

  • 模糊搜索:当我们要查找与搜索词相距一定距离的词时,可以使用模糊搜索。 为了理解差距,让我们考虑一个例子。 术语HibernateHibernat有1由于缺少差距e在后文中,术语HibernateHibernawe还具有以下在后面的字符串即单个字符1的缺口w可以更换成原字符串。
  • 通配符搜索:这些就像具有匹配短语的SQL语句一样。 像Hibernate匹配短语一样,可以是Hiberbernate等。

让我们在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语句一样工作:

通配符查询

@Override
    public 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> {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public 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");

	@Autowired
	private PersonService service;

	public static void main(String[] args) {
		SpringApplication.run(DataJpaApp.class, args);
	}

	@Override
	public 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值