Given a Hibernate Entity Foo
#Entity
#Table(name = "foo")
class Foo {
//ID Field
#Column(name = "bar", nullable = false)
#Enumerated(EnumType.STRING)
private Bar bar;
//Getters, Setters
}
And an Enum Bar
enum Bar {
CAT,
CRADLE,
SILVER,
SPOON;
}
I would like to run a query checking if Foo.bar's value contains some String matchString = "ADL".
Currently, my DetachedCriteria object is constructed as:
DetachedCriteria.forClass(Foo.class)
.add(Restrictions.like("bar", matchString, MatchMode.ANYWHERE));`
but causes a java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Enum when invoked.
A query like this would only be possible if the enum is persisted using the String value rather than the ordinal - but even then I would not expect the criteria API to necessarily support a like for enums.
Instead of trying to do the like at the database level you can find all the enums where the name contains "bar" (in Java code) and then query for all the matches where "bar" is in that set. This is similar to https://stackoverflow.com/a/18899529/4248600.
Disjunction or = Restrictions.disjunction();
for (BarEnum bar : BarEnum.values()) {
if (bar.name().contains(matchStr)) {
or.add(Restrictions.eq("bar", bar));
}
}
criteria.add(or);
Related
The Problem with PersistentBag is that it violates the Collections API equals() contract, which breaks unit test assertions via AssertJ's containsExactlyInAnyOrder(). Hibernate also has different wrapper implementations such as PersistentList, is there a way to force Hibernate (version 5.6.7) to use those instead of PersistentBag?
#Entity
data class AnalysisType(
#Id
name: String,
#Column(name = "reference_type")
#OneToMany(fetch = LAZY, cascade = [])
val referenceTypes: List<ReferenceType>
)
#Entity
data class ReferenceType(
#Id
val id: Long,
#Column
val name: String
)
I think there's something wrong with the unit tests. Comparing two collections using an equals doesn't seem the right approach. In particular, when using unordered lists, elements might not always be in the same order.
You also need to make sure that you've implemented the correct hashcode/equals for the entities.
In java, for ReferenceType, it should look like:
#Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !( o instanceof ReferenceType ) ) {
return false;
}
ReferenceType rt = (ReferenceType) o;
return Objects.equals( name, rt.name );
}
#Override
public int hashCode() {
return Objects.hash( name );
}
That said, if you want to have a PersistentList, the mapping needs to reflect an ordered list. You can achieve this using #OrderColumn:
#Entity(name = "Person")
public static class Person {
#Id
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#OrderColumn
private List<Phone> phones = new ArrayList<>();
//Getters and setters are omitted for brevity
}
You can find more in the Hibernate ORM documentation in the section about collections of entities.
If you are using containsExactlyInAnyOrder from assertj the order of the elements does not matter. You can check for duplicates. If the PersistentBag contains the same element twice you should use containsOnly which does not care about the order and is ignoring duplicates. The equals method on the PersistentBag should not matter too because the contains... assertions in assertj are comparing element by element for the collections.
It looks like they may be sth wrong in your tests. Can you post assertj message?
I am using Spring with JPA 2.0 and Hibernate 4.2.19.Final and I am trying to build a dynamic query which has simple predicates generated from the Restrictions class like method.
Restrictions.like("attributes.value" + value.getKey() , value.getValue());. My entity is stored in a sparse table where the number of the column relates to an attribute description.
Entity:
#Entity
#Table(name = "MY_ENTITIES")
public class Entity {
#Id
#GeneratedValue
Long id;
#Embedded
Attributes attributes;
}
#Embeddable
public class Attributes{
/** Attribute 1. */
#Embedded
#Column(name = "value_1")
private String attribute1;
*
/** Attribute N. */
#Embedded
#Column(name = "value_N")
private String attributeN;
}
There are complex predicates such as AND, OR, NOT predicates which are obtained by nesting the simple predicate described above.
Everything seems to work well when I am using AND and OR predicates but with more complex expressions which involves a NOT, such as for example:
((attribute1=% OR attribute2=%) AND attribute3=%) AND NOT attribute4=%
the Criteria.list() method returns empty list when there are entities in the DB satisfying the criteria.
Any suggestions?
Apparently the criterion:
Restrictions.not(Restrictions.like("attributes.value" + value.getKey(), value.getValue()));
returns false if the value in the table is null.
Fixed this by modifying my simple criterion like this:
Restrictions.and(Restrictions.isNotNull("attributes.value" + value.getKey()),
Restrictions.like("attributes.value" + value.getKey(), value.getValue()));
I have an #Entity class Person, that has multiple fields and I would like to map the result of several #NamedNativeQuerys to the Person class however, the queries I am running do not return values for every field in the Person class. When I try to run a query I get the following errors:
[error] o.h.u.JDBCExceptionReporter - Invalid column name bar.
[error] play - Cannot invoke the action, eventually got an error: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query
My class is set up similar to this:
#NamedNativeQueries({
#NamedNativeQuery(
name = "getBirthdate",
//Returns values for {idnumber, name, birthdate}
query = "EXEC dbo.proc_get_birthdate :name",
resultClass = Person.class
),
#NamedNativeQuery(
name = "getBar",
//Returns values for {idnumber, name, bar}
query = "EXEC dbo.proc_get_bar :name",
resultClass = Person.class
)
})
#Entity
public class Person {
#Id
#Column(name = "idnumber")
private int idNumber;
#Column(name = "name")
private String name;
#Column(name = "birthdate")
private String birthdate;
#Column(name = "foo")
private String foo;
#Column(name = "bar")
private String bar;
//All appropriate getter and setter methods are implemented
}
I double checked and all the columns in the Person class do, in fact, exist in the table being queried.
My actual class is much larger and due to that and some security concerns, I do not want my queries to have to return EVERY field and am hoping that there is a simple way to just give them a value of null if a value isn't returned by the query. I tried to set each field to null in the declaration (example below) but that didn't work either.
#Column(name = "bar")
private String bar = null;
I would really rather not have to create a tailored class for every single query I need to run so if what I'm trying to do is possible, any help would be greatly appreciated. Thanks in advance.
I believe that you misunderstood the #NamedNativeQuery use. This annotation should be used with a SQL Query and not with a stored procedure. Looking at the exception SQLGrammarException: could not execute query it is possible to see that is a grammar exception, the query could not be parsed.
With JPA 2.1 you could use the annotation #NamedStoredProcedureQuery that is the one with support to stored procedures.
I am having trouble using a native query in hibernate to alias a bean that contains enum properties. I am getting an InvocationTargetException when query.list() is called. My example is below:
#Entity(name = "table1")
public class Class1 {
#Column(name = "col1")
#NotNull
private Integer prop1;
#Column(name = "col2")
#NotNull
private String prop2;
#Column(name = "col3", length = 6)
#Enumerated(value = EnumType.STRING)
private MyEnumType prop3;
// ... Getters/Setters...
}
public List getClass1List(){
String sql = "select col1 as prop1, col2 as prop2, col3 as prop3 from table1";
Session session = getSession(Boolean.FALSE);
SQLQuery query = session.createSQLQuery(sql);
query.addScalar("col1", Hibernate.INTEGER);
query.addScalar("col2", Hibernate.STRING);
query.addScalar("col3", Hibernate.STRING);
query.setResultTransformer(Transformers.aliasToBean(Class1.class));
return query.list();
}
During the query.addScalar("col3", Hibernate.STRING) call, I don't know what type to use for col3 (the enum type). Hibernate.String is not working! I have also tried to leave the type off entirely ( query.addScalar("col3") ) but I get the same InvocationTargetException. Can anyone help me out with this? My model cannot be changed and I am stuck with a native sql query. Any ideas are appreciated.
// In public List getClass1List() method:
// 1. DEFINE:
Properties params = new Properties();
params.put("enumClass", "enumerators.MyEnumType");
params.put("type", "12");
// 2. REPLACE:
// query.addScalar("col3", Hibernate.STRING);
// X
query.addScalar("col3", Hibernate.custom(org.hibernate.type.EnumType.class, params));
Firstly, you shouldn't use
private EnumType prop3;
but
private ActualEnum prop3;
Where ActualEnum is your own enum type (for example, Fruits to distinguish apples and oranges).
Second, you hibernate mapping is irrelevant when you use native sql.
Now, there are couple of options I can propose. You can try to use addEntity() instead of bunch of scalars. It's possible that Hibernate will recognize enum property and map correctly.
Other option is to have non public setter that would take string from database, convert it to enum and set actual property.
Finally, you can customize transformer. But it's probably most complex option.
I have two objects that look something like this:
public class Foo{
List<Bar> bars;
String name;
}
public class Bar {
String value;
}
I would like to use the Hibernate Criteria API to make an OR query that involves both of the properties of Foo. Specifically, I want to select all the Foos that have the name "somevalue" OR have a Bar in their bars collection that has a value of "anothervalue".
I know how to do these independently:
createCriteria(Foo.class).add(Restrictions.eq("name","somevalue"));
and
createCriteria(Foo.class).createCriteria("bars").add(Restrictions.eq("value","anothervalue"));
(taken from here)
but when I try to put these on either side of a Restrictions.or() statement, it gives me a compile error. So, my question is: is this even possible using Criteria, or will I have to use HQL or plain SQL?
Just from the top of my head, would this work?
Criteria criteria = session.createCriteria(Foo.class);
criteria.createAlias("bars", "bar");
String value = "somevalue";
criteria.add(Restrictions.or(Restrictions.eq("name", value), Restrictions.eq("bar.name", value)));