Criteria API in hibernate 5 - hibernate-criteria

I am migrating from Hibernate 3 to Hibernate 5 and have the following function
public static <T extends Saveable> T searchByNaturalKeys(T entity, Map<String, Object>
naturalKeysMap)
throws Exception{
// CriteriaQuery<?> criteria = null;
try {
Session session = getSession();
NaturalIdentifier naturalIdentifier = new NaturalIdentifier();
for (Entry<String, Object> entry : naturalKeysMap.entrySet()) {
naturalIdentifier.set(entry.getKey(), entry.getValue());
}
criteria = session.createCriteria(entity.getClass()).add(naturalIdentifier);
}
I tried to do the same thing for Hibernate 5 here is my approach
criteria = builder.createQuery(entity.getClass());
Root<?> root = criteria.from(entity.getClass());
criteria.where(builder.equals(naturalKeysMap));
List<?> categories = session.createQuery(criteri).getSingleResult();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<?> criteriaQuery = criteriaBuilder.createQuery(entity.getClass());
return (T) criteria.uniqueResult();
I know I am doing something wrong here. Can anybody give me right approach for this?
Another approach to do above thing is also welcomed

Related

Mockito Predicate JPA not run correctly

In Unit Test Class:
#Mock
EntityManager entityManager;
#Mock
Root<RedFox> redFoxRoot;
#Mock
Expression expression;
#Mock
Path path;
#Mock
Predicate predicate;
#Test
public void filterTest(){
TypedQuery<RedFox> redFoxQuery = (TypedQuery<RedFox>) Mockito.mock(TypedQuery.class);
CriteriaQuery<RedFox> redFoxCriteriaQuery = Mockito.mock(CriteriaQuery.class);
Mockito.when(redFoxCriteriaQuery.where(Mockito.any(Predicate[].class)))
.thenReturn(redFoxQuery);
Mockito.when(redFoxCriteriaQuery.orderBy(Mockito.anyList())).thenReturn(redFoxCriteriaQuery);
CriteriaBuilder builder = Mockito.mock(CriteriaBuilder.class);
Mockito.when(redFoxRoot.get("isDeleted")).thenReturn(path);
Mockito.when(path.isNull()).thenReturn(predicate);
Mockito.when(builder.literal(Mockito.any())).thenReturn(expression);
Mockito.when(redFoxCriteriaQuery.from(RedFox.class)).thenReturn(redFoxRoot);
Mockito.when(entityManager.createQuery(redFoxCriteriaQuery)).thenReturn(redFoxQuery);
List<RedFox> redFoxs = new ArrayList<>();
RedFox redFox = new RedFox();
redFox.setTitle("abc");
redFox.setRedFoxStatus("");
redFox.setId(UUID.randomUUID());
redFoxs.add(redFox);
Mockito.when(redFoxQuery.getResultList()).thenReturn(redFoxs);
}
In class service:
public List<RedFox> filter(){
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<RedFox> redFoxCriteriaQuery = builder.createQuery(RedFox.class);
Root<RedFox> root = redFoxCriteriaQuery.from(RedFox.class);
redFoxCriteriaQuery.select(root);
List<Predicate> searchClause = new ArrayList<>();
searchClause.add(builder.isFalse(root.get("isDeleted")));//root cause error
Predicate[] searchClauses = searchClause.toArray(Predicate[]::new);
Query executingQuery = em.createQuery(redFoxCriteriaQuery.where(searchClauses)
.orderBy(orderBys));//Null pointer exception
List<RedFox> resultData = executingQuery
.getResultList();
..............................
}
My problem is when I comment the searchClause.add(builder.isFalse(root.get("isDeleted"))), it mean that the searchClauses is empty Array, then the code execute successfully.
But when the search clauses is not empty, then redFoxCriteriaQuery.where(searchClauses) not use the result I mocked but return null, and it will cause the null pointer exception.
I've been tucked 2 days but still don't know why so please help me with this issue.
Change
Mockito.when(redFoxCriteriaQuery.where(Mockito.any(Predicate[].class)))
.thenReturn(redFoxQuery);
to
Mockito.when(redFoxCriteriaQuery.where((Predicate[].class) Mockito.any()))
.thenReturn(redFoxQuery);
So, it can work with any non null object.
But if you write Mockito.any(Predicate[].class), then it just compare 2 array with type Predicate. And of course, 2 array cannot equal although all the elements in each array are equal.

Using Spock to test Spring JdbcTemplate

I have the following Java 8 DAO method that uses Spring JdbcTemplate to hit a database:
class MyAppDao {
#Inject
DataSource dataSource
public List<String> getFizzByBuzzIds(List<Integer> buzzIds) {
String sql = "SELECT * from buzzes WHERE buzz_id IN ( :buzzIds )";
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
Map<String, Object> paramValues = new HashMap<>(1);
paramValues.put("buzzIds", buzzIds);
List<Buzz> buzzes = jdbcTemplate.query(sql,
paramValues, (rs, rowNum) -> {
return new Buzz(rs.getString("buzz_id"), rs.getString("buzz_key"),
rs.getString("buzz_external_id"));
}
);
List<String> fizzes = figureOutSomehow();
return fizzes;
}
}
I cannot change the dependencies injected into this method. The NamedParameterJdbcTemplate instance must be defined inside this method. The only thing I can mock (easily) is the dataSource.
I am trying (and I know, the method isn't written to be conducive to unit tests at all!) to write a Spock unit test for this, and just want to verify that when getFizzByBuzzIds is called, that jdbcTemplate.query is called (with any arguments).
My best attempt:
def 'getFizzByBuzzIds attempts to get case info from the DB'() {
given:
MyAppDao dao = Spy(MyAppDao, constructorArgs: [ dataSource ]) {
getJdbcTemplate() >> Mock(JdbcTemplate) {
1 * query('', null) // ???
}
}
when:
List<Integer> buzzIds = [1, 2, 3, 4]
dao.getFizzByBuzzIds(buzzIds)
then:
1 * dao.jdbcTemplate.query('', null) // ???
}
How can I configure the Spy and the then block label so that executing getFizzByBuzzIds verifies whether the underlying JdbcTemplate.query(...) method is called?

Neo4j Spring Data Query Builder

Is there a way of dynamically building a cypher query using spring data neo4j?
I have a cypher query that filters my entities similar to this one:
#Query("MATCH (n:Product) WHERE n.name IN {0} return n")
findProductsWithNames(List<String> names);
#Query("MATCH (n:Product) return n")
findProductsWithNames();
When the names list is empty or null i just want to return all products. Therefore my service impl. checks the names array and calls the correct repository method. The given example is looks clean but it really gets ugly once the cypher statements are more complex and the code starts to repeat itself.
You can create your own dynamic Cypher queries and use Neo4jOperations to execute them. Here is it an example (with a query different from your OP) that I think can ilustrate how to do that:
#Autowired
Neo4jOperations template;
public User findBySocialUser(String providerId, String providerUserId) {
String query = "MATCH (n:SocialUser{providerId:{providerId}, providerUserId:{providerUserId}})<-[:HAS]-(user) RETURN user";
final Map<String, Object> paramsMap = ImmutableMap.<String, Object>builder().
put("providerId", providerId).
put("providerUserId", providerUserId).
build();
Map<String, Object> result = template.query(query, paramsMap).singleOrNull();
return (result == null) ? null : (User) template.getDefaultConverter().convert(result.get("user"), User.class);
}
Hope it helps
Handling paging is also possible this way:
#Test
#SuppressWarnings("unchecked")
public void testQueryBuilding() {
String query = "MATCH (n:Product) return n";
Result<Map<String, Object>> result = neo4jTemplate.query(query, Collections.emptyMap());
for (Map<String, Object> r : result.slice(1, 3)) {
Product product = (Product) neo4jTemplate.getDefaultConverter().convert(r.get("n"), Product.class);
System.out.println(product.getUuid());
}
}

List of instances with minimal date of their group

I'm working on a Java project, using Hibernate to administrate data on a SQL database.
I try to fetch a list of instances from the Database, that have a minimal timestamp of the group they share. The group is modeled by a container.
Here is a minimal model sketch:
#Entity
#Table(name = "object")
public class Object implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.Auto)
long obj_id;
#Column(name = "time_stamp", nullable = false)
Date timestamp;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "container_id", nullable = false)
Container con;
}
#Entity
#Table(name = "container")
public class Container{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long con_id;
#OneToMany(mappedBy = "container")
List<object> obj_list;
}
So there are some objects with a timestamp and containers that group these objects.
For example, there are two containers, con_a and con_b:
Container con_a:
con_id = 1
obj_list = {obj_a, obj_b}
Container con_b:
con_id = 2
obj_list = {obj_c}
And three objects, obj_a, obj_b, obj_c:
Object obj_a
obj_id = 1
timestamp = 10
con = con_a
Object obj_b
obj_id = 2
timestamp = 20
con = con_a
Object obj_c
obj_id = 3
timestamp = 30
con = con_b
The desired List in this example would look like:
List<Object> = {obj_a, obj_c}
I seem to move in a circle, as I do not even know where to "start" the query:
Criteria crit = session.createCriteria(Container.class). ...
or
Criteria crit = session.createCriteria(Object.class). ...
It seems both possible for me, but i just have no idea how to go on from any of those 2 possibilities.
Update [2014.07.11, 14:19]:
I tried and started the query with the Object class and used a Subquery:
Session session = getSession();
Transaction transaction = session.beginTransaction();
DetachedCriteria IdListOfGroupMinimum = DetachedCriteria.forClass(Object.class, "obj")
IdListOfGroupMinimum.createAlias("con.id", "containerId")
.setProjection(
.Projections.projectionList()
.add(Projections.property("obj.id"))
.add(Projections.min("obj.timestamp"))
.add(Projections.groupProperty("containerId")))
.setProjection(Projection.property("obj.id"));
Criteria objects = session.createCriteria(object.class, "obj")
objects.add(Subqueries.in("obj.id", IdListOfGroupMinimum));
List<Object> = objects.list();
But I received the following error:
javax.servlet.ServletException: org.hibernate.QueryException: not an association: id
I tried to do this:
SELECT * from Object
WHERE id IN (
SELECT obj.id
FROM Object obj
INNER JOIN (
SELECT obj.containerID, MIN(obj.timestamp) AS minimum
FROM Object obj
GROUP BY obj.containerID) subquery
ON obj.containerID = subquery.containerID
WHERE obj.timestamp = subquery.minimum
)
I found a solution for my problem which is probably not the most elegant one, but it works.
Mainly I used the SQL-Query that I already posted above:
Session session = getSession();
Transaction transaction = session.beginTransaction();
//This query fetches the IDs of the smallest objects in each group with
//regard to the timestamp
Query q = session.createSQLQuery(
"SELECT obj.id FROM Object obj "
+ "INNER JOIN ( "
+ "SELECT obj.containerID, MIN(obj.timestamp) AS minimum "
+ "FROM Object obj "
+ "GROUP BY obj.containerID) subquery "
+ "ON obj.containerID = subquery.containerID "
+ "WHERE obj.timestamp = subquery.minimum "
);
//This tells Hibernate that the result are values of type Long
q.addScalar("id", LongType.INSTANCE)
//Creates a list of the found IDs
#SuppressWarnings("unchecked")
List<Long> ids = q.list();
//Fetches all object with those IDs...
Criteria smallestOfEachGroup = session.createCriteria(Object.class)
.add(Restrictions.in("id", ids);
//...and saves them in a list.
#SuppressWarnings("unchecked")
List<Object> desiredList = smallestOfEachGroup.list()
try{
transaction.commit();
} catch(HibernateException e) {
transaction.rollback();
}
As all my sketches are not the real code, so there might be still naming errors.
Anyway, I hope this helps someone.
I still would be pleased by any more elegant solution.
Update [2014.07.20, 18:50]:
I found a solution that uses Hibernate Criteria exclusively :)
Session session = getSession();
Transaction transaction = session.beginTransaction();
//This subquery fetches the minimal timestamp of a container.
DetachedCriteria minOfGroup = DetachedCriteria.forClass(Object.class);
minOfGroup.add(Restrictions.eqProperty("con.con_id", "outerObject.con.con_id")
.setProjection(Projections.min("timestamp"));
//This subquery fetches the IDs of all Objects, whose timestamp is minimal
//in their container.
DetachedCriteria groupwiseMin = DetachedCriteria.forClass(Object.class, "outerObject");
groupwiseMin.add(Subqueries.propertyEq("timestamp", minOfGroup));
.setProjections(Projections.id())
//This subquery fetches all Objects whose IDs are fetched by the groupwiseMin
//query
Criteria groupwiseMinObjects = session.createCriteria(Object.class);
groupwiseMinObjects.add(Subqueries.propertyIn("obj_id", groupwiseMin));
List<Object> desiredObjects = groupwiseMinObjects.list();
try{
transaction.commit();
} catch(HibernateException e) {
transaction.rollback();
}
I think you can make this query even shorter, if you remove the groupwiseMinObjects query above replace the groupwiseMin query by:
Criteria anotherGroupWiseMinObjects = session.createCriteria(Object.class, "outerObject");
anotherGroupwiseMinObjects.add(Subqueries.propertyEq("timestamp", minOfGroup));
But I did not test that.
In my original project I use several subqueries that converge in a single query.
That means after some subqueries, there is a final query like:
Criteria finalQuery = session.createCriteria(Object.class);
finalQuery.add(Subqueries. (...) )
(...)
.add(Subqueries. (...) );

count * with where clause using JPA criteria query

I want to find records for related_elements table, where relationId belongs to a list
suppose a Set tempSet contains[2,3,4]
I have to check for these value contains in related_element table using jpa criteria query
CriteriaBuilder cb1=entityManager.getCriteriaBuilder();
CriteriaQuery<RelatedElements> cq1=cb1.createQuery(RelatedElements.class);
Root<RelatedElements> RelatedElementsRoot=cq1.from(RelatedElements.class);
for (Integer tSet : tempSet) {
ParameterExpression<Integer> pRelatedElement=cb1.parameter(Integer.class);
cq1.multiselect(cb1.count(RelatedElementsRoot.<RelatedElements>get("relatedElementsPk").<Integer>get("relationId"))).where(cb1.equal(RelatedElementsRoot.get("relationId"), pRelatedElement));
TypedQuery<RelatedElements> qry = entityManager.createQuery(cq1);
qry.setParameter(pRelatedElement, tSet);
count = entityManager.createQuery(cq1).getSingleResult().getRelationId();
}
but its now working...any suggessions
second try
CriteriaBuilder cb1=entityManager.getCriteriaBuilder();
CriteriaQuery<Integer> cq1 = cb1.createQuery(Integer.class);
Root<RelatedElements> RelatedElementsRoot=cq1.from(RelatedElements.class);
for (Integer tSet : tempSet) {
ParameterExpression<Integer> pRelatedElement=cb1.parameter(Integer.class);
cq1.multiselect(cb1.count(cq1.from(RelatedElements.class)));
cq1.where((cb1.equal(RelatedElementsRoot.get("relatedElementsPk").get("relationId"), pRelatedElement)));
TypedQuery<Integer> qry = entityManager.createQuery(cq1);
qry.setParameter(pRelatedElement, tSet);
count =qry.getSingleResult();
}
its giving exception at qry.setParameter
Unable to locate appropriate constructor on class [java.lang.Integer] [select new java.lang.Integer(count(*)) from com.mcd.webex.model.RelatedElements as generatedAlias0, com.mcd.webex.model.RelatedElements as generatedAlias1 where generatedAlias0.relatedElementsPk.relationId=:param0]
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<Dzialy> root = cq.from(Dzialy.class);
EntityType <Dzialy> Dzialy_ = root.getModel();
cq.select((cb.countDistinct(root)));
cq.where( cb.equal( root.get(Dzialy_.getSingularAttribute("DZI_id")), 1) );
long l = em.createQuery(cq).getSingleResult();
em - EntityManager
DZI_id - column name
1 - searching value
Dzialy - entity class
As documented, CriteriaBuilder.count returns Expression<java.lang.Long>. Consequently type argument to CriteriaQuery and TypedQuery should be Long as well. Same holds true for type of count variable.
When there is only one value to be selected, then it makes sense to use CriteriaQuery.select instead of multiselect, because then such an error is catched already in compile time.
Long count;
...
CriteriaQuery<Long> cq1 = cb1.createQuery(Long.class);
...
cq1.select(cb1.count(cq1.from(RelatedElements.class)));
...
TypedQuery<Long> qry = entityManager.createQuery(cq1);
CriteriaBuilder qb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = qb.createQuery(Long.class);
cq.select(qb.count(cq.from(YourEntity.class)));
cq.where(/*your stuff*/);
return entityManager.createQuery(cq).getSingleResult();