Поиск в спящем режиме возвращает пустые результаты при использовании весенней информационной системы, развернутой локально с помощью tomcat - PullRequest
0 голосов
/ 11 января 2019

У меня следующая проблема: я разрабатываю информационную систему на основе Spring, используя Hibernate, MySql5.5, Java 7 и Apache Tomcat 7.0. Требование, которое я должен выполнить, указывает на использование поиска для поиска задач. Эти задачи должны быть найдены с использованием keyword, max date, min date, max price, min price, category name и warranty title. keyword должно присутствовать в любом из следующих атрибутов задачи: ticker (буквенно-цифровой идентификатор), description и address. Диапазон цен применяется к атрибуту задачи price, а диапазон дат - как к 1013 *, так и к end date задачи. Конечно, и category name, и warranty title применяются к тем, которые связаны с определенной задачей

Я разработал реализацию этого искателя, используя поиск в режиме гибернации, и когда я выполняю тест jUnit для своего искателя, он фактически возвращает правильный список задач. Проблема возникает, когда я пытаюсь протестировать этот искатель с информационной системой, развернутой локально с использованием Tomcat. Даже когда я использую те же параметры, что и в тесте jUnit, результатом является пустой список. Кроме того, всякий раз, когда я выполняю отправку формы редактирования, которая представляет поиск задачи, моя консоль затмения выдает следующее предупреждающее сообщение: [http-bio-8080-exec-9] WARN org.hibernate.jpa.internal.EntityManagerFactoryRegistry - HHH00043 6: Entity manager factory name (Acme-HandyWorker) is already registered. If entity manager will be clustered or passiva ted, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'. Я не знаю, как это предыдущее предупреждение влияет на мою проблему.

Здесь представлена ​​модель домена UML ранее указанных объектов


Задача

@Indexed
@Entity
@Access(AccessType.PROPERTY)
public class Task extends DomainEntity {

private String ticker;
private Date publicationMoment;
private String description;
private String address;
private double maxPrice;
private Date startDate;
private Date endDate;
private Warranty warranty;
private Category category;
private Collection<Complaint> complaints;
private Customer customer;
private Collection<Application> applications;

@Field
@NotBlank
@Column(unique = true)
@Pattern(regexp = "^[0-9]{6}-[A-Z0-9]{6}$")
public String getTicker() {
return this.ticker;
}

public void setTicker(final String ticker) {
this.ticker = ticker;
}

@Past
@NotNull
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "dd/MM/yyyy HH:mm")
public Date getPublicationMoment() {
return this.publicationMoment;
}

public void setPublicationMoment(final Date publicationMoment) {
this.publicationMoment = publicationMoment;
}

@Field
@NotBlank
public String getDescription() {
return this.description;
}

public void setDescription(final String description) {
this.description = description;
}

@Field
@NotBlank
public String getAddress() {
return this.address;
}

public void setAddress(final String address) {
this.address = address;
}

@Min(0)
@Digits(integer = 99, fraction = 2)
@Field
@NumericField
public double getMaxPrice() {
return this.maxPrice;
}

public void setMaxPrice(final double maxPrice) {
this.maxPrice = maxPrice;
}

@Past
@NotNull
@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd/MM/yyyy")
@Field
public Date getStartDate() {
return this.startDate;
}

public void setStartDate(final Date startDate) {
this.startDate = startDate;
}

@NotNull
@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd/MM/yyyy")
@Field
public Date getEndDate() {
return this.endDate;
}

public void setEndDate(final Date endDate) {
this.endDate = endDate;
}

@Valid
@ManyToOne(optional = false)
@IndexedEmbedded
public Warranty getWarranty() {
return this.warranty;
}

public void setWarranty(final Warranty warranty) {
this.warranty = warranty;
}

@Valid
@ManyToOne(optional = false)
@IndexedEmbedded
public Category getCategory() {
return this.category;
}

public void setCategory(final Category category) {
this.category = category;
}

@NotNull
@OneToMany
public Collection<Complaint> getComplaints() {
return this.complaints;
}

public void setComplaints(final Collection<Complaint> complaints) {
this.complaints = complaints;
}

@Valid
@ManyToOne(optional = false)
public Customer getCustomer() {
return this.customer;
}

public void setCustomer(final Customer customer) {
this.customer = customer;
}

@NotNull
@OneToMany(mappedBy = "task")
public Collection<Application> getApplications() {
return this.applications;
}

public void setApplications(final Collection<Application> applications) {
this.applications = applications;
}

}

гарантия

@Entity
@Access(AccessType.PROPERTY)
public class Warranty extends DomainEntity {

private String              title;
private Collection<String>  terms;
private Collection<String>  laws;
private String              mode;


@NotBlank
@Field
public String getTitle() {
    return this.title;
}

public void setTitle(final String title) {
    this.title = title;
}

@NotEmpty
@ElementCollection
public Collection<String> getTerms() {
    return this.terms;
}

public void setTerms(final Collection<String> terms) {
    this.terms = terms;
}

@NotEmpty
@ElementCollection
public Collection<String> getLaws() {
    return this.laws;
}

public void setLaws(final Collection<String> laws) {
    this.laws = laws;
}

@NotBlank
@Pattern(regexp = "^(DRAFT|FINAL)$")
public String getMode() {
    return this.mode;
}

public void setMode(final String mode) {
    this.mode = mode;
}

}

Категория

@Entity
@Access(AccessType.PROPERTY)
public class Category extends DomainEntity {

private String      name;
private String      nameEs;
private Category    father;


@NotBlank
@Field
public String getName() {
    return this.name;
}

public void setName(final String name) {
    this.name = name;
}

@NotBlank
@Field
public String getNameEs() {
    return this.nameEs;
}

public void setNameEs(final String nameEs) {
    this.nameEs = nameEs;
}

@Valid
@ManyToOne(optional = true)
public Category getFather() {
    return this.father;
}

public void setFather(final Category father) {
    this.father = father;
}

}

Finder

@Entity
@Access(AccessType.PROPERTY)
public class Finder extends DomainEntity {

private String keyWord;
private Double minPrice;
private Double maxPrice;
private Date minDate;
private Date maxDate;
private String categoryName;
private String warrantyTitle;
private Collection<Task> tasks;
private Date moment;

@NotNull
public String getKeyWord() {
return this.keyWord;
}

public void setKeyWord(final String keyWord) {
this.keyWord = keyWord;
}

@Min(0)
@Digits(integer = 99, fraction = 2)
public Double getMinPrice() {
return this.minPrice;
}

public void setMinPrice(final Double minPrice) {
this.minPrice = minPrice;
}

@Min(0)
@Digits(integer = 99, fraction = 2)
public Double getMaxPrice() {
return this.maxPrice;
}

public void setMaxPrice(final Double maxPrice) {
this.maxPrice = maxPrice;
}

@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd/MM/yyyy")
public Date getMinDate() {
return this.minDate;
}

public void setMinDate(final Date minDate) {
this.minDate = minDate;
}

@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd/MM/yyyy")
public Date getMaxDate() {
return this.maxDate;
}

public void setMaxDate(final Date maxDate) {
this.maxDate = maxDate;
}

@NotNull
public String getCategoryName() {
return this.categoryName;
}

public void setCategoryName(final String categoryName) {
this.categoryName = categoryName;
}

@NotNull
public String getWarrantyTitle() {
return this.warrantyTitle;
}

public void setWarrantyTitle(final String warrantyTitle) {
this.warrantyTitle = warrantyTitle;
}

@NotNull
@ManyToMany
public Collection<Task> getTasks() {
return this.tasks;
}

public void setTasks(final Collection<Task> tasks) {
this.tasks = tasks;
}

@NotNull
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "dd/MM/yyyy")
public Date getMoment() {
return this.moment;
}

public void setMoment(final Date moment) {
this.moment = moment;
}

}

pom.xml

<!-- Hibernate -->

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.3.0.Final</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>4.3.1.Final</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-c3p0</artifactId>
        <version>4.2.3.Final</version>
    </dependency>

    <!-- Hibernate Full-text search -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-search</artifactId>
        <version>4.5.3.Final</version>
    </dependency>

persistence.xml

<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

<persistence-unit name="Acme-HandyWorker">
    <properties>
        <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/Acme-HandyWorker" />
        <property name="javax.persistence.jdbc.user" value="acme-manager" />
        <property name="javax.persistence.jdbc.password" value="ACME-M@n@ger-6874" />

        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
        <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />

        <!-- Hibernate Full-text search -->
        <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.impl.FSDirectoryProvider"/>
        <property name="hibernate.search.default.indexBase" value="var/lucene/indexes"/>
    </properties>

</persistence-unit>

Выполнение фильтра задач

public List<Task> filterTasks() {
    final Finder f = this.finderService.findOne();

    final String keyword = f.getKeyWord().toLowerCase();
    final double minPrice = f.getMinPrice();
    final double maxPrice = f.getMaxPrice();
    final Date minDate = f.getMinDate();

    final Date maxDate = f.getMaxDate();
    final String category = f.getCategoryName().toLowerCase();
    final String warranty = f.getWarrantyTitle().toLowerCase();

    final ConfigurationParameters conf = this.configurationParametersService.find();
    final int max = conf.getMaxResults();

    final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Acme-HandyWorker");
    final EntityManager em = entityManagerFactory.createEntityManager();
    final FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);

    em.getTransaction().begin();

    final QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Task.class).get();

    final org.apache.lucene.search.Query checkKeyword = qb.bool().should(qb.keyword().wildcard().onField("ticker").matching("*" + keyword + "*").createQuery())
        .should(qb.keyword().wildcard().onField("description").matching("*" + keyword + "*").createQuery()).should(qb.keyword().wildcard().onField("address").matching("*" + keyword + "*").createQuery()).createQuery();

    final org.apache.lucene.search.Query checkCategory = qb.bool().should(qb.keyword().wildcard().onField("category.name").matching("*" + category + "*").createQuery())
        .should(qb.keyword().wildcard().onField("category.nameEs").matching("*" + category + "*").createQuery()).createQuery();

    final org.apache.lucene.search.Query query = qb.bool().must(checkKeyword).must(qb.range().onField("maxPrice").from(minPrice).to(maxPrice).createQuery()).must(qb.range().onField("startDate").above(minDate).createQuery())
        .must(qb.range().onField("endDate").below(maxDate).createQuery()).must(checkCategory).must(qb.keyword().wildcard().onField("warranty.title").matching("*" + warranty + "*").createQuery()).createQuery();

    final FullTextEntityManager fullTextSession = Search.getFullTextEntityManager(fullTextEntityManager);
    final org.hibernate.search.jpa.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query);

    fullTextQuery.setMaxResults(max);

    // execute search
    final List result = fullTextQuery.getResultList();

    em.getTransaction().commit();
    em.close();

    return result;
}

jUnit test

    @Test
public void testSave() {
    super.authenticate("handyworker40");

    final Finder f = this.finderService.findOne();
    f.setMaxPrice(120.);
    final String maxDateInString = "20/11/2018";
    final Date maxDate = this.defaultDate(maxDateInString);
    final String minDateInString = "06/04/2018";
    final Date minDate = this.defaultDate(minDateInString);
    f.setMaxDate(maxDate);
    f.setMinDate(minDate);

    final Finder saved = this.finderService.save(f);

    final Collection<Finder> fs = this.finderService.findAll();
    System.out.println(saved.getTasks());

    Assert.isTrue(fs.contains(saved));

}
private Date defaultDate(final String dateInString) {
    final SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
    Date res = new Date();
    try {
        res = formatter.parse(dateInString);
    } catch (final ParseException e) {
        throw new RuntimeException(e);
    }

    return res;
}

Sysout, напечатанный в консоли:

[domain.Task{id=3804, version=0}, domain.Task{id=3807, version=0}, domain.Task{id=3813, version=0}, domain.Task{id=3818,
 version=0}, domain.Task{id=3819, version=0}, domain.Task{id=3826, version=0}, domain.Task{id=3827, version=0}]

Контроллер поиска

@RequestMapping(value = "/edit", method = RequestMethod.GET)
public ModelAndView edit() {
    ModelAndView result;
    Finder finder;
    final boolean clear;

    finder = this.finderService.findOne();

    clear = this.finderService.clearCache(finder);

    if (clear) {
        final Collection<Task> empty = new ArrayList<>();
        finder.setTasks(empty);
    }

    result = this.createEditModelAndView(finder);

    return result;
}

@RequestMapping(value = "/edit", method = RequestMethod.POST, params = "find")
public ModelAndView save(@Valid final Finder finder, final BindingResult binding) {
    ModelAndView result;

    if (binding.hasErrors())
        result = this.createEditModelAndView(finder);
    else
        try {
            this.finderService.save(finder);
            result = new ModelAndView("redirect:display.do");
        } catch (final Throwable oops) {
            result = this.createEditModelAndView(finder, "finder.commit.error");
        }

    return result;
}

protected ModelAndView createEditModelAndView(final Finder finder) {
    ModelAndView result;

    result = this.createEditModelAndView(finder, null);

    return result;
}

protected ModelAndView createEditModelAndView(final Finder finder, final String messageCode) {
    final ModelAndView result;
    Collection<Warranty> warranties;
    Collection<Category> categories;
    double maxPrice;
    String lang;

    lang = LocaleContextHolder.getLocale().getLanguage();

    warranties = this.warrantyService.findAll();
    categories = this.categoryService.findAll();
    maxPrice = this.taskService.findMaxPrice();

    result = new ModelAndView("finder/edit");
    result.addObject("finder", finder);
    result.addObject("warranties", warranties);
    result.addObject("categories", categories);
    result.addObject("maxPrice", maxPrice);
    result.addObject("lang", lang);

    result.addObject("message", messageCode);

    return result;
}

Метод сохранения службы поиска

    public Finder save(final Finder f) {
    Assert.notNull(f);

    final HandyWorker principal = this.handyWorkerService.findByPrincipal();
    final Date moment = new Date(System.currentTimeMillis());
    f.setMoment(moment);

    if (f.getMaxDate().equals(null)) {
        final String maxDateInString = "31/12/9999";
        final Date maxDate = this.defaultDate(maxDateInString);
        f.setMaxDate(maxDate);
    } else if (f.getMinDate().equals(null)) {
        final String minDateInString = "31/12/999";
        final Date minDate = this.defaultDate(minDateInString);
        f.setMinDate(minDate);
    } else if (f.getMaxPrice().equals(null)) {
        final double maxPrice = this.taskService.findMaxPrice();
        f.setMaxPrice(maxPrice);
    } else if (f.getMinPrice().equals(null))
        f.setMinPrice(0.);

    final Finder saved = this.finderRepository.save(f);

    if (f.getId() == 0) {
        principal.setFinder(saved);
        this.handyWorkerService.save(principal);
    } else
        Assert.isTrue(saved.equals(f));

    final Collection<Task> filteredTasks = this.taskService.filterTasks();
    saved.setTasks(filteredTasks);

    return this.finderRepository.save(saved);
}

edit.jsp

<form:form action="finder/handyWorker/edit.do" modelAttribute="finder">

<form:hidden path="id" />
<form:hidden path="version" />
<form:hidden path="tasks" />
<form:hidden path="moment" />

<form:label path="keyWord">
    <spring:message code="finder.keyWord" />:
</form:label>
<form:input path="keyWord"/>
<br/>

<form:label path="minPrice">
    <spring:message code="finder.minPrice" />:
</form:label>
<form:input path="minPrice" type="number" min="0" max="${maxPrice}"/>
<form:errors cssClass="error" path="minPrice" />
<br />

<form:label path="maxPrice">
    <spring:message code="finder.maxPrice" />:
</form:label>
<form:input path="maxPrice" type="number" min="0" max="${maxPrice}"/>
<form:errors cssClass="error" path="maxPrice" />
<br />

<form:label path="minDate">
    <spring:message code="finder.minDate" />:
</form:label>
<form:input path="minDate"/>
<form:errors cssClass="error" path="minDate" />
<br />

<form:label path="maxDate">
    <spring:message code="finder.maxDate" />:
</form:label>
<form:input path="maxDate"/>
<form:errors cssClass="error" path="maxDate" />
<br />

<form:label path="categoryName">
    <spring:message code="finder.categoryName" />:
</form:label>
<form:input path="categoryName"/>
<br/>

<form:label path="warrantyTitle">
    <spring:message code="finder.warrantyTitle" />:
</form:label>
<form:input path="warrantyTitle"/>
<br/>

<input type="submit" name="find" value="<spring:message code="finder.save" />" />

Здесь вы можете увидеть вид редактирования с теми же параметрами, что и в тесте jUNit

И это результат просмотра результатов просмотра: результат

1 Ответ

0 голосов
/ 14 января 2019

Во-первых, предупреждение: выполнение следующего фрагмента кода во время выполнения действительно сомнительно.

final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Acme-HandyWorker");

Вы создаете фабрику менеджера сущностей каждый раз, когда выполняется метод filterTasks(). Но фабрика управления сущностями предназначена для использования в качестве единого элемента, повторно используемого во всех ваших приложениях в каждом потоке. Как правило, у вас не должно быть более одного экземпляра фабрики диспетчера сущностей для данной единицы сохраняемости.

Я рекомендую вам использовать платформу, которая управляет фабриками менеджеров сущностей и менеджерами сущностей для вас. Spring Boot - это одно, WildFly - другое, но я уверен, что практически любой фреймворк, который вы найдете, сделает это за вас.

Если вы действительно хотите обработать его самостоятельно, то хотя бы создайте фабрику диспетчера сущностей при загрузке приложения, сохраните ее где-нибудь для извлечения вашими методами времени выполнения (например, filterTasks()) и обязательно закройте ее, когда ваш приложение закрывается.

О вашей проблеме

Наиболее вероятным объяснением является то, что вы не проиндексировали свои данные. В этом тесте вы сохраняете данные в своей базе данных, поэтому Hibernate Search будет получать события записи и индексировать на лету. В производстве данные (насколько я понимаю) уже сохранены, поэтому Hibernate Search не имел возможности их проиндексировать.

Чтобы проиндексировать то, что уже есть в базе данных, взгляните на массовое индексирование ; по сути, вам нужно будет выполнить нечто подобное при первой загрузке приложения:

EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
fullTextEntityManager.createIndexer().startAndWait();

Существует множество вариантов массового индексатора, пожалуйста, обратитесь к документации, чтобы использовать то, что лучше для вас.

На заметку

На всякий случай, если вы создаете новое приложение, я хотел бы отметить, что используемые вами версии Hibernate ORM и Hibernate Search устарели, поэтому вы, скорее всего, столкнетесь с ошибками, которые были исправлены в более поздних версиях. В новом приложении вы, вероятно, должны использовать более свежие версии. Хотя я понимаю, что у вас может не быть выбора, особенно если это устаревшее приложение.

Если вы застряли на Java 7, я бы посоветовал перейти на Hibernate ORM 5.1 и Hibernate Search 5.6. В идеале вы должны рассмотреть возможность обновления до Java 8, ORM 5.4 и Search 5.11. Миграция, вероятно, потребует изменений в вашем приложении, но на hibernate.org доступны руководства по миграции, здесь для Hibernate Search и [здесь для Hibernate ORM]) (http://hibernate.org/orm/documentation/5.4/) (для ORM, Вы должны выбрать версию, используя поле выбора в правом верхнем углу).

...