我有以下问题:我正在使用Hibernate,MySql5.5,Java 7和Apache Tomcat 7.0开发基于Spring的信息系统。我必须满足的要求指定了使用查找程序来查找任务。必须使用keyword
,max date
,min date
,max price
,min price
,category name
和{ {1}}。 warranty title
必须出现在以下任何任务属性中:keyword
(字母数字标识符),ticker
和description
。价格范围适用于任务属性address
,日期范围适用于任务的price
和start date
。当然,end date
和category name
都适用于与某个任务相关的任务
我已经使用休眠搜索开发了该查找器的实现,当我为查找器执行jUnit测试时,它实际上会返回正确的任务列表。当我尝试使用使用Tomcat本地部署的信息系统测试此查找器时,就会出现问题。即使使用与jUnit测试中相同的参数,结果仍然是一个空列表。另外,每当我执行代表任务搜索的编辑表单的提交时,Eclipse控制台都会引发以下警告消息:warranty title
。我不知道先前的警告如何影响我的问题。
Here you have the UML domain model of the entities previously specified
[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'
@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;
}
}
@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;
}
}
<!-- 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 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;
}
在控制台中打印出sysout:
@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;
}
[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);
}
Here you can see the editing view with the same parameters as in the jUNit test
这是显示结果视图中的结果:result
答案 0 :(得分:0)
首先,一个警告:在运行时执行以下代码确实是可疑的。
final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Acme-HandyWorker");
每次执行filterTasks()
方法时,您正在创建一个实体管理器工厂。但是,实体管理器工厂旨在成为一个单例,可以在每个线程的所有应用程序中重复使用。对于给定的持久性单元,通常不应有多个实体管理器工厂实例。
我鼓励您使用为您管理实体管理器工厂和实体管理器的框架。 Spring Boot是其中的一个,WildFly是另一个,但是我敢肯定,您会发现几乎任何框架都可以为您做到这一点。
如果您真的想自己处理它,那么至少在启动应用程序时创建实体管理器工厂,将其存储在某个位置以供运行时方法(例如filterTasks()
)检索,并确保将其关闭当您的应用程序关闭时。
最可能的解释是您没有索引数据。在测试中,您将数据持久保存到数据库中,因此Hibernate Search将即时获取写入事件和索引。在生产中,数据(据我了解)已经存在,因此Hibernate Search没有机会对其进行索引。
要索引数据库中已有的内容,请查看mass indexing;基本上,您在首次启动应用程序时将需要运行以下操作:
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,here for Hibernate Search和[这里是Hibernate ORM])(http://hibernate.org/orm/documentation/5.4/)上有可用的迁移指南(对于ORM,您必须选择一个版本(使用右上角的选择框)。