Hibernate criteria for a foreign key list - java

I have two entities:
public class Document implements java.io.Serializable {
private Long id;
private String info;
private Set<Tag> tags = new HashSet<Tag>(0);
}
public class Tag implements java.io.Serializable {
private Long id;
private String name;
private Set<Document> documents = new HashSet<Document>(0);
}
A document may have more than one tag, and each tag can contain many items.
Now I want to do a filter function to find out all the documents that has both tag1(id = 1) and tag2(id = 2).
I tried to use these restrictions:
Criteria criteria = session.createCriteria(Document.class, "doc")
.createAlias("doc.tags", "tag");
List<Document> docList = criteria.add(Restrictions.eq("tag.id", 1))
.add((Restrictions.eq("tag.id", 2)).list();
but they're not working, the list is empty. Is there a good solution?

You are looking for a tag with id equal to 1 AND 2. It's not possible use Restrictions.in("tag.id", Arrays.asList(1, 2))

Related

OneToMany return just specific rows

I have class with attribute
#Entity
public class Energy() {
#Id
private long id;
private Date date;
#OneToMany(fetch = FetchType.LAZY)
private List<Value> values;
}
and Value class have
private String obis;
private long value;
What is the fastest way to return just elements of list values where obis contains specific value?
Thanks!
It depends on the implementation you choose, for example with Criteria Api:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Energy> cq = cb.createQuery(Energy.class);
Root<Energy> root = cq.from(Energy.class);
Join<Energy,Value> join = root.join(Energy_.values);
//If you dont use metamodel change by root.join("values")
cq.select(root);
cq.where(cb.equal(join.get(Value_.value),VALUE));
//If you dont use metamodel change by join.get("value")
List<Energy> result = entityManager.createQuery(cq).getResultList();

Two #ElementCollections from one table with discriminator column style implementation

Firstly, I don't know if this is possible, as i think the solution would combine two existing JPA features into one: Element Collections and Discriminator Columns.
As stated below, i know how to achieve the solution using ONLY discriminators, however this would be a cumbersome implementation given the requirements stated below.
I have a Wiget pojo, that will have optional filters to reduce the content displayed. At the moment, i have two pojos that can be used as filters (there will be more) the classes below are simplified versions of the classes (they contain a lot more, but I've stripped them down to the relevant information):
Cell:
#Entity
#Table(name = "Cell")
public class Cell implements java.io.Serializable {
private Integer cellId;
private String cell;
private String description;
...
}
Task:
#Entity
#Table(name = "Task")
public class Task implements java.io.Serializable {
private Integer taskId;
private String task;
...
}
What i want to achieve is have one Table (a #CollectionTable?) that contains references to both of these pojos without the need for a "join pojo". Unless I've misunderstood, this is the point of an #ElementCollection.
The number of filter pojos to be filtered on will be expanded, and could potentially incorporate most of the pojos already in the system, i don't want to have to create multiple "join" pojos just to maintain all possible filters, when i could instead just maintain some static integers that reference the pojo type in a similar way that a discriminator column works
So in the Widget, it would look like:
Widget:
#Entity
#Table(name = "Widget")
public class Widget {
private Integer widgetId;
private String colour;
private String description;
private Date fromDate;
private Date toDate;
private Set<Cell> cellsFilter = new HashSet<Cell>();
private Set<Task> tasksFilter = new HashSet<Task>();
...
}
The filters (cellsFilter and tasksFilter) are optional (could have no filters at all).
And the Table that represents the #CollectionTable would look like:
╔═════════════════╗
║ WidgetFilters ║
╠═════════════════╣
║ id ║
║ widgetId ║ <- Should join to the Widget.widgetId column
║ pojoType ║ <- Integer, 1 = cell, 2 = task etc...
║ pojoId ║ <- Should join on the id of the cell or task (or other)
╚═════════════════╝
I've done a similar thing with #DiscriminatorColumn, however this would require me to create an abstract Filter object, CellFilter and TaskFilter (and one for every other filter eventually created).
The closest example i can find is https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection however this is only linking one pojo type (Phone) with one join (the employee id), i need to link to pojoId to the correct pojo type based on the pojoType column AND join it to the Widget based on the widgetId.
FYI, I've tried things like joins with where clauses, various combinations:
#ElementCollection(targetClass=Cell.class)
#JoinTable(
name="WidgetFilter",
joinColumns=#JoinColumn(name="widgetId", referencedColumnName="widgetId"),
inverseJoinColumns=#JoinColumn(name="pojoId", referencedColumnName="cellId")
)
#Where(clause = "pojoType = 1")
public Set<Cell> getCellsFilter() {
return cellsFilter;
}
This is a difficult concept to google, given all the "similar, but not quite" answers available
If it's not possible, that's fine, at least i know how to proceed, but if anyone has achieved this already, any help would be appreciated.
My Current Solution:
The only way i can think of doing this would be to create just one Filter pojo that represents the WidgetFilter Table row, and make the Widget pojo include logic that resolves a List<Filters> into the correct types itself (without any fancy annotations) like so...
Widget:
#Entity
#Table(name = "Widget")
public class Widget {
private Integer widgetId;
private String colour;
private String description;
private Date fromDate;
private Date toDate;
private Set<Filter> filters = new HashSet<Filter>();
private Set<Cell> cellsFilter = new HashSet<Cell>();
private Set<Task> tasksFilter = new HashSet<Task>();
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "filter")
public List<Filter> getFilters() {
return this.filters;
}
public void setFilters(List<Filter> filters) {
this.filters = filters;
setCellFilters(filters);
setTaskFilters(filters);
}
#Transient
public List<Cell> getCellsFilter() {
return this.cellsFilter;
}
public void setCellsFilter() {
List<Filter> cellFilters = filters.stream().filter(f -> f.getPojoType().intValue() == Filter.CELL).collect(Collectors.toList());
// get List<Cell> from database from List<Filter>
}
#Transient
public List<Task> getTasksFilter() {
return this.tasksFilter;
}
public void setTasksFilter() {
List<Filter> taskFilters = filters.stream().filter(f -> f.getPojoType().intValue() == Filter.TASK).collect(Collectors.toList());
// get List<Task> from database from List<Filter>
}
...
}
Filter:
#Entity
#Table(name = "Filter")
public class Filter{
public static final int CELL = 1;
public static final int TASK = 2;
private Integer filterId;
private Widget widget;
private Integer pojoType;
private Integer pojoId;
...
}
Cheers,
Steve.

How to use AND in hibernate 5.2 Criteria?

I have two objects User and House. One user can have several houses.
Annotations from House.class:
#Entity
#Table(name="house")
public class House {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="house_id")
private int id;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
#Column(name="country")
private String country;
#Column(name="city")
private String city;
#Column(name="street")
private String street;
#Column(name="post_code")
private String postCode;
#Column(name="house_number")
private String houseNumber;
#Column(name="flats")
private int flats;
#Column(name="picture")
private String picture;
#Column(name="surname_first") // order names will be written in receipt
private Boolean writeSurnameFirst;
#Column(name="cut_name") // Nado ili net sokrashat imena
private Boolean cutName;
#ManyToOne
#JoinColumn(name="currency_id")
private Currency currency;
#ManyToOne
#JoinColumn(name="documentation_lang_id")
private Languages documentationLang;
#Column(name="default_house")
private Boolean defaultHouse;
I need to get a House object based on User and column defaultHouse = true.
I tried this code, but I can't get how to implement User into it:
tx = sess.beginTransaction();
// create criteria builder
CriteriaBuilder builder = sess.getCriteriaBuilder();
// create criteria
CriteriaQuery<House> query = builder.createQuery(House.class);
// specify criteria root
Root<House> root = query.from(House.class);
query.select(root).where(builder.equal(root.get("default_house"), true)
.and(builder.equal(root.get(House.getUser), user)));
house = sess.createQuery(query).getSingleResult();
tx.commit();
In fact builder.and() takes two parameters which are the two restrictions to be joined with AND keyword in SQL, and the builder.and() should be used inside builder.where() method.
change the following code:
query.select(root).where(builder.equal(root.get("default_house"), true)
.and(builder.equal(root.get(House.getUser), user)));
Like this:
query.select(root).where(builder.and(builder.equal(root.get("default_house"), true), builder.equal(root.get(House.getUser), user))));
Please refer to Hibernate ORM 5.2.11.Final User Guide for further details and more examples.
Note:
Hibernate is concerned about objects and not tables and when we use its modules like Criteria we use attributes names in the Object and not DB columns names.
In CriteriaBuilder methods you must use the entity field names like following:
builder.equal(root.get("defaultHouse"), true)
and not the database column names:
builder.equal(root.get("default_house"), true)
I attached my example:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Eje> query = builder.createQuery(Eje.class);
Root<Eje> productRoot = query.from(Eje.class);
query.select(productRoot)
.where(builder.and(builder.equal(productRoot.get("id"), 1), builder.equal(productRoot.get(Eje_.factorCriticos).get(FactorCritico_.id), 1)));
result =(List<T>) session.createQuery(query).getResultList();

ORMLite - OneToOne relation

I got next database structure with OneToOne relation:
[company]
company_id (PK)
company_name
[company_configuration]
company_configuration_id (Autoincrement, PK)
company_id (UNIQUE KEY,FK)
company_configuration_v
I have been using ORMlite and I have next classes for this two tables:
#DatabaseTable(tableName = "company")
public class Company {
public static final String ID_COMPANY = "company_id";
public static final String COMPANY_NAME = "company_name";
#DatabaseField(generatedId = true, columnName = ID_COMPANY)
private int idCompany;
#DatabaseField(columnName = COMPANY_NAME)
private String companyName;
#DatabaseTable(tableName = "company_configuration")
public class CompanyConfiguration {
public static final String COMPANY_CONFIGURATION_ID = "company_configuration_id";
public static final String COMPANY_ID = "company_id";
public static final String COMPANY_CONFIGURATION_V = "company_configuration_v";
#DatabaseField(generatedId = true, columnName = COMPANY_CONFIGURATION_ID)
private int idCompanyConfiguration;
#DatabaseField(foreign = true,foreignAutoRefresh = true, columnName = COMPANY_ID)
private Company companyId;
#DatabaseField(columnName = COMPANY_CONFIGURATION_V)
private String companyConfigurationV;
Here is OneToOne relation because I want to divide a table with many columns.
As you can see in the example above, there is not relation from Company class to CompanyConfiguration class.
I know that I can add this snippet of code(examle below) into Company class, but I don't need a #ForeignCollectionField becaues the collection will contain only one CompanyConfiguration object:
#ForeignCollectionField()
private ForeignCollection<CompanyConfiguration> companyConfigurations;
I need to add something like this (examle below) into Company class and will get the reference from Company class to CompanyConfiguration class:
#OneToOne(targetEntity = CompanyDbConfig.class)
#JoinTable(name = "company_configuration")
#JoinColumn(name = "id_company")
CompanyConfiguration companyConfiguration;
Shortly, I want to get Company object using ORMlite. See the example below. After fetching company from the database, I want to have and CompanyConfiguration object within company object.
Company company = daoCompany.queryForId(id); //daoCompany is an instance of ORMlite Dao class
Is it possible and how to do that using ORMlite?
I posted an OrmLite question myself so I looked through the unanswered questions to see if there was anything I could answer. Even though this is an old topic, I wanted to take a stab at it in case it could help someone.
I've read your post a few times and I think you're asking how to load the information from two tables into one model. You're separating a rather large table into two in the database but you want it to come back as one model. If that is correct, here's my take on the code. This assumes you want to use objects to build the query instead of passing in a query string.
public class CompanyResult
{
public long CompanyId { get; set; }
public long ConfigurationId { get; set; }
public string Name { get; set; }
public string ConfigurationV { get; set; }
}
var query = _db.From<Company>
.Join<CompanyConfiguration>((c, cc) => c.idCompany == cc.idCompany)
.Where(c => c.idCompany == companyId)
.Select<CompanyConfiguration>((c, cc) = new {
CompanyId = c.idCompany,
ConfigurationId = cc.idCompanyConfiguration,
Name = c.companyName,
ConfigurationV - cc.companyConfigurationV
});
var results = _db.Single<CompanyResult>(query);
You'd keep your existing models so they could be used as DTOs. You'd just be using the new model model above to pass back the exact properties you want.
*I wrote this in Notepad++, forgive any typos.

Hibernate criteria: search by content on a property list in an entity

I want to search by content on a property in an entity
I have a simple class to define a User:
#Entity
public class User {
#Id
#Column(name = "pers_id")
private int persId;
#Column(name = "full_name")
private String fullName;
#OneToMany
#JoinColumn(name = "PERS_ID")
private List<UserLanguages> languages = new ArrayList<UserLanguages>();
}
A User can have multiple languages, here is the class to make the link between user and a language.
#Entity
public class UserLanguages {
#Column(name="pers_id")
private int persId;
#Id
#Column(name="lang_iso_code")
private String langISO;
#Column(name="lang_full_name")
private String langFullName;
#Column(name="order_seq")
private int order;
}
#Entity
public class Language {
#Id
#Column(name="ID")
private long id;
#Column(name = "CODE")
private String code;
}
I have created a object to do search:
public class UserFilter {
private String name;
private List<Language> languages;
}
I have defined a service:
#Service("userService")
public class UserServiceImpl implements UserService {
#Override
public List<User> findByFilter(UserFilter userFilter) {
final Criteria criteria = userDao.createCriteria();
if (userFilter.getName() != null) {
for (final String token : userFilter.getName().toLowerCase().trim().split(" ")) {
criteria.add(Restrictions.like("fullName", "%" + token + "%"));
}
}
if (null != userFilter.getLanguages() && userFilter.getLanguages().size() > 0) {
final List<String> contents = new ArrayList<String>(userFilter.getLanguages().size());
for (final Language lang : userFilter.getLanguages()) {
contents.add(lang.getCode());
}
criteria.add(Restrictions.in("languages", contents));
}
return userDao.findByCriteria(criteria);
}
My question is how can I do search on languages. I want to find all users with this or thoses languages defined in the userFilter param.
The part about languages doesn't work in the method findByFilter in the service. Can you help me?
First of all, the UserLanguages entity should be named UserLanguage : it represents one language, and not several.
Then, the pers_id column is a foreign key to the User entity. It should thus be mapped as a ManyToOne relationship to the User entity rather than a basic column.
Finally, and to answer your question (I'll assume you want to find the users having at least one user language whose langISO code is in the contents list) : you should use a join :
// inner join between User and UserLanguages
criteria.createAlias("languages", "userLanguage");
// restriction on the langISO property of UserLanguage
criteria.add(Restrictions.in("userLanguage.langIso", contents));

Categories