I have a lucene query that makes fulltext search on indexed fields. I want to add date range to this query.
I found that question and used the answer there:
How to search between dates (Hibernate Search)?
But when I want to get data between dates it returns nothing.
I am using MSSQL db and type of date field is datetime.
But it is annotated as #Temporal(TemporalType.TIMESTAMP) in entity class.
Here is my entity class:
...
#Entity
#Indexed
#FullTextFilterDef(name = "tarihAraligi", impl = Satislar.class)
#Table(name = "satislar")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Satislar.findAll", query = "SELECT s FROM Satislar s"),
#NamedQuery(name = "Satislar.findById", query = "SELECT s FROM Satislar s WHERE s.id = :id"),
#NamedQuery(name = "Satislar.findByAdet", query = "SELECT s FROM Satislar s WHERE s.adet = :adet"),
#NamedQuery(name = "Satislar.findByTarih", query = "SELECT s FROM Satislar s WHERE s.tarih = :tarih"),
#NamedQuery(name = "Satislar.findByUrunadi", query = "SELECT s FROM Satislar s WHERE s.urunadi = :urunadi"),
#NamedQuery(name = "Satislar.findByMusteriadi", query = "SELECT s FROM Satislar s WHERE s.musteriadi = :musteriadi"),
#NamedQuery(name = "Satislar.findByUrunkategori", query = "SELECT s FROM Satislar s WHERE s.urunkategori = :urunkategori"),
#NamedQuery(name = "Satislar.findByUrunfiyat", query = "SELECT s FROM Satislar s WHERE s.urunfiyat = :urunfiyat"),
#NamedQuery(name = "Satislar.findByUrunalis", query = "SELECT s FROM Satislar s WHERE s.urunalis = :urunalis")})
public class Satislar implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#DocumentId
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "adet")
private int adet;
#Basic(optional = false)
#Column(name = "tarih")
#Temporal(TemporalType.TIMESTAMP)
private Date tarih;
#Basic(optional = false)
#Column(name = "urunadi")
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String urunadi;
#Basic(optional = false)
#Column(name = "musteriadi")
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String musteriadi;
#Basic(optional = false)
#Column(name = "urunkategori")
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.NO)
private String urunkategori;
#Basic(optional = false)
#Column(name = "urunfiyat")
private int urunfiyat;
#Basic(optional = false)
#Column(name = "urunalis")
private int urunalis;
...
And this is where I make fulltext search:
fullTextSession.beginTransaction();
QueryBuilder b = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Satislar.class).get();
Query luceneQuery
= b.keyword()
.wildcard()
.onFields(fields)
.matching(kelime + "*")
.createQuery();
Query datequery = b
.range()
.onField( "tarih" ).ignoreFieldBridge()
.from( DateTools.dateToString( new Date(2015, 12, 18 , 17, 40, 40), DateTools.Resolution.MILLISECOND ) )
.to( DateTools.dateToString( new Date(2015, 12, 26 , 17, 40, 40), DateTools.Resolution.MILLISECOND ) ).excludeLimit()
.createQuery();
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(datequery);
List<Satislar> kayitliSatislar = fullTextQuery.list();
dtmSonuc.setRowCount(0);
for (int i = 0; i < kayitliSatislar.size(); i++) {
Satislar satis = kayitliSatislar.get(i);
dtmSonuc.addRow(new String[]{satis.getMusteriadi(), satis.getUrunkategori(), satis.getUrunadi(),
Integer.toString(satis.getAdet()), Integer.toString(satis.getUrunfiyat()), satis.getTarih().toString()});
}
There are 2 different queries. One makes fulltext wildcard search on specified fields and it works. And the other is supposed to make range search but it does not work
I have 3 questions:
1-I am using MSSQL db and type of date field is datetime.
But it is annotated as #Temporal(TemporalType.TIMESTAMP) in entity class. Is that a problem?
2-Why range search is not working?
3-Can I combine fulltext search with range search?
Try to add this annotation to your Date fields :
#Basic(optional = false)
#Column(name = "tarih")
#Temporal(TemporalType.TIMESTAMP)
#DateBridge(resolution = Resolution.DAY)
private Date tarih;
If it is still not working, try to use Lucene native queries like this :
TermRangeQuery luceneRangeDateQuery = new TermRangeQuery(fieldName, DateTools.dateToString(from, DateTools.Resolution.DAY),
DateTools.dateToString(to, DateTools.Resolution.DAY), true, true);
This works always. (You can change your DateTools.Resolution.DAY to HOUR, SECOND, YEAR ...)
Related
I'm trying to build a complex query with Criteria.
I created the query in SQL in order to explain you exactly what I want:
SELECT ID, DATE
FROM SELLS v
WHERE DATE > (SELECT min(DATE)
FROM SELLS
WHERE CODE IN (12345, 12344)
AND ID = v.ID)
AND DATE>TO_DATE('2015-01-01', 'YYYY-MM-DD')
AND DATE<TO_DATE('2016-01-01', 'YYYY-MM-DD')
GROUP BY ID, DATE
Here is my class:
#Entity
#Table(name = "SELLS")
#Getter
public class Sells {
#Id
#Column(name = "SELL_ID")
private Integer idSell;
#Column(name = "ID")
private Integer id;
#Column(name = "DATE")
private Date date;
#Column(name = "CODE")
private String code;
}
And what I tried in Criteria (start and end are the two dates in the WHERE ('2015-01-01' and '2016-01-01'):
Criteria c = getSession().createCriteria(Sells.class);
List subquery = getSession().createCriteria(Sells.class)
.add(Restrictions.in("code", {"12345", "12344"}))
.createAlias("Sells.id", "id")
.setProjection(Projections.projectionList()
.add(Projections.min("date")))
.list();
c.add(Restrictions.ge("date", subquery));
c.add(Restrictions.ge("date", start));
c.add(Restrictions.le("date", end));
c.setProjection(Projections.projectionList()
.add(Projections.groupProperty("date"))
.add(Projections.groupProperty("id")));
I tried to use a DetachedCriteria too but failed.
DetachedCriteria query = DetachedCriteria.forClass(Sells.class, "v");
DetachedCriteria subquery = DetachedCriteria.forClass(Sells.class, "bv")
.add(Restrictions.in("bv.code", codes))
.add(Restrictions.eq("bv.id", "v.id")
.setProjection(Projections.projectionList()
.add(Projections.min("bv.date")));
query.add(Restrictions.ge("v.date", subquery))
.add(Restrictions.ge("v.date", start))
.add(Restrictions.le("v.date", end))
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("v.date"))
.add(Projections.groupProperty("v.id")));
If some one has an idea how to do that it would help me a lot !
EDIT: This looks better but still not working:
Criteria query = getSession().createCriteria(Sells.class, "v");
DetachedCriteria subquery = DetachedCriteria.forClass(Sells.class, "bv")
.add(Restrictions.in("bv.code", codes))
.add(Restrictions.eq("bv.id", "v.id")
.setProjection(Projections.projectionList()
.add(Projections.min("bv.date")));
query.add(Restrictions.ge("v.date", subquery))
.add(Restrictions.ge("v.date", start))
.add(Restrictions.le("v.date", end))
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("v.date"))
.add(Projections.groupProperty("v.id")));
org.hibernate.criterion.DetachedCriteria cannot be cast to java.util.Date
I have the following entity (example):
#Entity
#Table(name = "person")
public class Person implements Serializable {
#Id
#Column(name = "person_id", columnDefinition = "UUID")
private UUID userId;
#Column(name = "name")
private String name;
#ElementCollection
#MapKeyColumn(name = "phonetype")
#Column(name = "number")
#CollectionTable(name = "person_phones", joinColumns = #JoinColumn(name = "userId"))
private Map<String, String> phoneNumbers;
}
Now, the phoneNumbers are String, String in this example. Let's assume the key is the type (like "mobile", "home", "office", "fax", "pager"...) and the value is the actual number in any text format.
I'd like to query for a person which has two phone numbers:
Select * From person where
in his phone_numbers exists phonetype = 'home' and number = '0-123-456'
and also in his phone_numbers exists phonetype = 'mobile' and number = '9-876-421'
(and possibly, dynamically others)
and name = 'John'
I already constructed a sql subquery which works:
select home.userId from
(
(SELECT userId from person_phones
where (phonetype = 'home' and number = '0-123-456'))
) as home,
(
(SELECT userId from person_phones
where (phonetype = 'mobile' and number = '9-876-421'))
) as mobile
where home.userId = mobile.userId
As said, this is just a sql subquery. I'm writing JPA 2.1 criteria query in my project. And this seems oddly complicated. Can anyone give me a hint?
Had a similar problem, solved it using multiple inner joins rather than sub queries.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Person-Test");
EntityManager em = emf.createEntityManager();
Map<String, String> phoneNumbers = new HashMap<>();
phoneNumbers.put("home","0-123-456");
phoneNumbers.put("mobile","9-876-421");
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> personRoot = query.from(Person.class);
query.select(personRoot);
phoneNumbers.forEach((k, v) -> {
MapJoin<Person, String, String> phoneNrJoinJoin = personRoot.joinMap("phoneNumbers");
phoneNrJoinJoin.on(cb.equal(phoneNrJoinJoin.key(), k), cb.equal(phoneNrJoinJoin.value(), v));
});
query.where(cb.equal(personRoot.get("name"), "John"));
List<Person> people = em.createQuery(query).getResultList();
This results in the following hibernate query (renamed aliases for clarity)
SELECT person.person_id, person.name
FROM person
INNER JOIN person_phones a
ON person.person_id = a.userid
AND (a.phonetype = ? AND a.NUMBER = ?)
INNER JOIN person_phones b
on person.person_id=b.userId
and (b.phonetype=? and b.number = ? )
WHERE
person.name = ?;
Which returns all tuples of type person where all the mentioned phone numbers match.
I have table A which have relation with table B as one-to-many.
What I want to do is to do select with іщьу limit from table A and after join to selected results table B.
So I have faced with a problem how do that in the right way bu the hibernate?
Typical Criteria.selectMaxResults it is not that I need, cause limit will accept after join and instead of getting 10 different rows from table A I will get, for example, just 1 row from table 'A' with joined 10 different rows from table B.
(1) So what is the best way to do that? Select just unique rows from A in one query and in the other query do select from A with join B?
In general, on the native SQL language, I expect to execute the next query:
String sQuery = SELECT a.*,b* FROM (SELECT * FROM parentTable WHERE c.id=777 LIMIT 0,10) AS a, b.* WHERE a.id=b.a_id;
So, according to hibernate tutorial and docs I've tried to use session.createSQLQuery(sQuery). My method looks in the next way:
#Override
#Transactional
public List<VocabularyWord> getWords(int vocId, int firstResult, int pageSize) {
Session s = this.template.getSessionFactory().getCurrentSession();
SQLQuery query = s.createSQLQuery("SELECT {vW.*}, m.* FROM (SELECT * FROM vocabulary_words AS vw WHERE vw.vocabulary_id=:id LIMIT :from,:size) AS vW, meaning AS m WHERE vW.id=m.vocabulary_words_id;");
query.setInteger("id", vocId);
query.setInteger("from", firstResult);
query.setInteger("size", pageSize);
query.addEntity("vW", VocabularyWord.class);
query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
// If unccoment the next word I get error: Column 'id1_1_1_' not found. But as I'have understood I don't need this.
// query.addEntity("m", Meaning.class);
List l = query.list();
return l;
}
And this method returns exactly what I want. BUT to return this result hibernate executes a huge amount of queries. (He try to do something like (1)?). Being exactly Hibernate executes 5 next queries:
Hibernate:
SELECT
vW.id as id1_7_0_,
vW.original_text as original2_7_0_,
vW.transcription as transcri3_7_0_,
vW.vocabulary_id as vocabula4_7_0_,
m.*
FROM
(SELECT
*
FROM
vocabulary_words AS vw
WHERE
vw.vocabulary_id=? LIMIT ?,?) AS vW,
meaning AS m
WHERE
vW.id=m.vocabulary_words_id;
Hibernate:
select
meaning0_.vocabulary_words_id as vocabula5_1_0_,
meaning0_.id as id1_1_0_,
meaning0_.id as id1_1_1_,
meaning0_.definition as definiti2_1_1_,
meaning0_.example as example3_1_1_,
meaning0_.translation as translat4_1_1_,
meaning0_.vocabulary_words_id as vocabula5_1_1_,
meaning0_.w_type as w_type6_1_1_
from
meaning meaning0_
where
meaning0_.vocabulary_words_id=?
Hibernate:
select
meaning0_.vocabulary_words_id as vocabula5_1_0_,
meaning0_.id as id1_1_0_,
meaning0_.id as id1_1_1_,
meaning0_.definition as definiti2_1_1_,
meaning0_.example as example3_1_1_,
meaning0_.translation as translat4_1_1_,
meaning0_.vocabulary_words_id as vocabula5_1_1_,
meaning0_.w_type as w_type6_1_1_
from
meaning meaning0_
where
meaning0_.vocabulary_words_id=?
Hibernate:
select
meaning0_.vocabulary_words_id as vocabula5_1_0_,
meaning0_.id as id1_1_0_,
meaning0_.id as id1_1_1_,
meaning0_.definition as definiti2_1_1_,
meaning0_.example as example3_1_1_,
meaning0_.translation as translat4_1_1_,
meaning0_.vocabulary_words_id as vocabula5_1_1_,
meaning0_.w_type as w_type6_1_1_
from
meaning meaning0_
where
meaning0_.vocabulary_words_id=?
Hibernate:
select
meaning0_.vocabulary_words_id as vocabula5_1_0_,
meaning0_.id as id1_1_0_,
meaning0_.id as id1_1_1_,
meaning0_.definition as definiti2_1_1_,
meaning0_.example as example3_1_1_,
meaning0_.translation as translat4_1_1_,
meaning0_.vocabulary_words_id as vocabula5_1_1_,
meaning0_.w_type as w_type6_1_1_
from
meaning meaning0_
where
meaning0_.vocabulary_words_id=?
My VocabularyWord entity:
public class VocabularyWord implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private int id;
#Column(name = "vocabulary_id", updatable = false, nullable = false)
private int vocId;
#Column(name = "original_text", nullable = false)
private String originalText;
#Column(name="transcription", nullable = true)
private String transcription;
#OneToMany (mappedBy = "vocWord", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Meaning> meaning;
// + getters and setters
}
My Meaning entity:
public class Meaning implements Serializable {
#Id
#Column(name = "id", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "vocabulary_words_id", nullable = false, updatable = false)
private VocabularyWord vocWord;
#JsonIgnore
#Column(name = "vocabulary_words_id", updatable = false, insertable = false)
private int vocWordId;
#Column(name = "w_type", nullable = true, unique = false)
private String wType;
#Column(name = "example", nullable = true, unique = false)
private String example;
#Column(name = "definition", nullable = true, unique = false)
private String definition;
#Column(name = "translation", nullable = true, unique = false)
private String translation;
//+ getters and setters
}
How can I fix this? Or, probably, have I choose the other way to realize that query?
I will appreciate any help, propositions, ideas and related links. Thanks in advance.
I have resolved my problem by changing my query and adding .addJoin() and .addRootEntity(). So now my method getWords looks next:
#Override
#Transactional
public List<VocabularyWord> getWords(int vocId, int firstResult, int pageSize) {
Session s = this.template.getSessionFactory().getCurrentSession();
SQLQuery query = s.createSQLQuery("SELECT {vW.*}, {m.*} FROM (SELECT * FROM vocabulary_words AS vw WHERE vw.vocabulary_id=:id LIMIT :from,:size) AS vW LEFT JOIN meaning AS m ON (m.vocabulary_words_id = vW.id);");
query.addEntity("vW", VocabularyWord.class)
.addJoin("m", "vW.meaning")
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.setInteger("id", vocId)
.setInteger("from", firstResult)
.setInteger("size", pageSize);
query.addRoot("vW", VocabularyWord.class);
List l = query.list();
return l;
}
This method executes exactly one query:
Hibernate:
SELECT
vW.id as id1_7_0_,
vW.original_text as original2_7_0_,
vW.transcription as transcri3_7_0_,
vW.vocabulary_id as vocabula4_7_0_,
m.vocabulary_words_id as vocabula6_1_0__,
m.id as id1_1_0__,
m.id as id1_1_1_,
m.definition as definiti2_1_1_,
m.example as example3_1_1_,
m.translation as translat4_1_1_,
m.vocabulary_words_id as vocabula6_1_1_,
m.w_type as w_type5_1_1_
FROM
(SELECT
*
FROM
vocabulary_words AS vw
WHERE
vw.vocabulary_id=? LIMIT ?,?) AS vW
LEFT JOIN
meaning AS m
ON (
m.vocabulary_words_id = vW.id
);
I am new in Hibernate, I want to translate the JPQL into Criteria Query while I am learning the CriteriaQuery in Hibernate. I am successfully able to create the JPQL but got Stopped in the criteria. Following is my scenario:
Item Class:
#Entity
#Table(name = "ITEM")
public class Item {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "ITEM_ID")
private Long id = null;
#Version
#Column(name = "OBJ_VERSION")
private int version = 0;
#Column(name = "ITEM_NAME", length = 255, nullable = false, updatable = false)
private String name;
#ManyToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="itemSellerId")
private User seller;
#Transient
private User buyer;
........
User Class::
...
#OneToMany(fetch=FetchType.EAGER, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
private Set<Item> boughtItems = new HashSet<Item>();
..
My JPQL query which is running fine::
public List<Item> findBySellerOrBuyer(Long sellerId,Long buyerId) {
Query query = entityManager.createQuery("select distinct i from Item i, User u "
+ "where (i.initialPrice > 22 and i.seller.id = :sellerId) "
+ "OR "
+ "( u.id = :buyerId and i member of u.boughtItems and i.initialPrice = i.reservePrice )");
return (List<Item>) query.setParameter("sellerId", sellerId).setParameter("buyerId", buyerId).getResultList();
}
the condition is ::
A Criteria Query that gets Items that match:
a) The Seller & an initial price > 22.0
OR
b) The Buyer & a reserved price = initial price
thanks :)
Last Update
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Item> cr = builder.createQuery(Item.class);
Criterion price = Restrictions.gt("initial price", 22);
Criterion name = Restrictions.eq("seller.id","abc");
Criterion Buyer = Restrictions.eq("buyer.id","abc");
Criterion reserved_price = Restrictions.eq("reserved_price","initial_price");
// To get records matching with AND condition for price & name
LogicalExpression andExp = Restrictions.and(name, price);
LogicalExpression andExp2 = Restrictions.and(Buyer, reserved_price);
return (List<Item>) ((Criteria) cr).list();
Use below code -
Criteria cr = session.createCriteria(Item.class);
Criterion price = Restrictions.gt("initial price", 22);
Criterion name = Restrictions.eq("seller.id","abc");
Criterion Buyer = Restrictions.eq("buyer.id","abc");
Criterion reserved_price = Restrictions.eq("reserved_price","initial_price");
Disjunction objDisjunction = Restrictions.disjunction();
/* Add multiple condition separated by OR clause within brackets. */
objDisjunction.add(Restrictions.and(price , name ));
objDisjunction.add(Restrictions.and(Buyer, reserved_price));
cr.add(objDisjunction);
List results = cr.list();
My code looks like:
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final TypedQuery<ObjectTO> createQuery = criteriaBuilder.createQuery(ObjectTO.class);
...
// Logic to create search query
...
// On this line performance is very low. Takes around 200 seconds for 3000 records.
final List<ObjectTO> inhibits = createQuery.getResultList();
Generated SQL (example with reduced columns in select clause) looks like:
SELECT t1.COL1 ,
t1.COL2 ,
t1.COL3 ,
t2.COL1 ,
t2.COL2 ,
t3.COL2
FROM Table1 t1
INNER JOIN Table2 t2,
ON t1.COL1 = t2.COL1
LEFT OUTER JOIN Table3 t3
ON t1.COL1 = t3.COL1
WHERE (t1.COL2 LIKE 'test')
AND ( t1.COL3 >= SYSDATE
OR t1.COL3 = to_Date('1901.01.01 00:00:00', 'yyyy-mm-dd HH24:mi:ss') )
ORDER BY t1.COL1 ASC ,
t1.COL3 ASC ,
t2.COL2 ASC;
Same query when executed in SQL developer, takes around 0.3 seconds. Please note that same query if executed using java.sql.Statement.executeQuery(), takes around 0.4 seconds to fetch data from database. Please note that database is Oracle 11g. JDK version is 1.7.0_51.
JPA entity for Table1 looks like:
#Entity
#Table(name = "Table1")
public class Table1 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "COL1", insertable = false, updatable = false)
private String col1;
#Column(name = "COL3", insertable = false, updatable = false)
private Timestamp col3;
#Column(name = "COL2")
private String col2;
#OneToMany(mappedBy = "col1", cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
private Set<Table2> table2;
#OneToMany(mappedBy = "col1", cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
private Set<Table3> table3;
...
...
}
Can you please provide me with an hint regarding where shall I start looking to improve performance in this scenario? Please let me know, if you require more information.