Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 6 of 7
In the sample application, when search results are displayed as a list on a Web screen, leaving the resume Word documents
unfetched can significantly save memory. An applicant profile plus a summary of the Resume should provide enough information for users to make a selection. Once a user selects a row in the result list, the chosen
Word document will then be fetched from the BLOB database column and returned through a separate request/response.
The seFindResumeProjectionsWithoutDatabaseAccess() method, shown in Listing 9, demonstrates how to employ projections in Hibernate Search to make a reporting query without
database access. The method also shows how to get the relevance scores for Lucene search results. Keep in mind that the Resume objects in the results are not managed by the persistence context of JPA; therefore, they are detached JPA entities, with
all the fields populated from the Lucene search results. Note that you lose a crucial benefit when employing these detached
entities -- automatic dirty checking, a key factor that makes Hibernate backed applications perform great for data update
operations.
@SuppressWarnings("unchecked")
public Map<Resume, Float> seFindResumeProjectionsWithoutDatabaseAccess(
final Date beginDate, final Date endDate,
final String... keywordsInSummary) {
Object results = getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
FullTextEntityManager fullTextEntityManager = createFullTextEntityManager(em);
BooleanQuery bq = new BooleanQuery();
for (String q : keywordsInSummary) {
TermQuery tq = new TermQuery(new Term("summary", q));
bq.add(new BooleanClause(tq, BooleanClause.Occur.MUST));
}
FullTextQuery fq = fullTextEntityManager.createFullTextQuery(
bq, Resume.class);
FullTextFilter ff = fq.enableFullTextFilter("rangeFilter");
ff.setParameter("fieldName", "lastUpdated");
ff.setParameter("lowerTerm", DateTools.dateToString(beginDate,
DateTools.Resolution.DAY));
ff.setParameter("upperTerm", DateTools.dateToString(endDate,
DateTools.Resolution.DAY));
ff.setParameter("includeLower", true);
ff.setParameter("includeUpper", true);
fq.setProjection(FullTextQuery.SCORE, "id",
"summary",
"applicant.id", "applicant.firstName",
"applicant.lastName", "applicant.middleName",
"applicant.emailAddress");
Map<Resume, Float> resumes = new HashMap<Resume, Float>();
for (Object[] result : (List<Object[]>) fq.getResultList()) {
Resume resume = new Resume();
User applicant = new User();
resume.setApplicant(applicant);
resume.setId((Long) result[1]);
resume.setSummary((String) result[2]);
/** WordDoc content is left blank. */
applicant.setId((Long) result[3]);
applicant.setFirstName((String) result[4]);
applicant.setLastName((String) result[5]);
applicant.setMiddleName((String) result[6]);
applicant.setEmailAddress((String) result[7]);
resumes.put(resume, (Float) result[0]);
}
return resumes;
}
});
return (Map<Resume, Float>) results;
}
Service classes annotated by @service in Spring 2.5 are always a great place to specify the transaction characteristics of your Web applications. This is especially
true when managing XA transactions across multiple data sources. Different DAO classes may collaborate to complete an XA transaction
defined in a single method of a service class. In the ResumeManagerImpl class, shown in Listing 10, each business method is marked with the Spring @Transactional annotation, with propagation, readyOnly, and isolation as attributes. Don't confuse this with the EJB 3 @TransactionAttribute annotation, although both are designed for a similar goal. Hibernate Search by default encapsulates Lucene indexing processes
with database transactions; hence, indexes are only updated when database operations are committed.
package demo.hibernatesearch.service.impl;
@Service("resumeManager")
public class ResumeManagerImpl implements ResumeManager {
protected final Log log = LogFactory.getLog(getClass());
@Autowired
private ResumeDao resumeDao;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, isolation = Isolation.READ_COMMITTED)
public void saveApplicant(User applicant) {
resumeDao.saveApplicant(applicant);
}
//...
}
Another advantage of the newly released Spring 2.5 annotations: they enable POJO test cases, as illustrated in Listing 11.
By default, each test method in a test class runs under a single transaction context, and the transaction rolls back at the
end of the method. In the sample application, this default behavior has been suppressed with a @Rollback(false) annotation on each test method, so that the results are committed to the database and Lucene indexes. Spring's JUnit/TestNG
extensions allow you to run your test cases out of the container. All you need to do is to execute the Maven 2 built-in lifecycle
phase -- mvn test.
package demo.hibernatesearch.dao;
import static junit.framework.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class })
@ContextConfiguration(locations = "/WEB-INF/applicationContext*.xml")
@Transactional
public class ResumeDaoTest {
@Autowired
private ResumeDao resumeDao;
//...
@Test
@Rollback(false)
public void testSeFindResumesWithDocHandler() throws Exception {
List<Resume> resumes = resumeDao.seFindResumesWithDocHandler(
new GregorianCalendar(2006, 1, 1).getTime(),
new GregorianCalendar().getTime(), "java", "web");
assertTrue(resumes.size() == 5);
}
//...
}
Archived Discussions (Read only)