I have the following entity (not exact but gives a general idea):
#Entity
public class WebElement implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
private Set<CoreElement> coreElements;
private String agent;
// ... omitting const' get/set hashcode equals etc.
}
public class CoreElement implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String value;
// ... omitting const' get/set hashcode equals etc.
}
My problem is when trying to fetch WebElements using the Criteria API vs. HQL
When executing the following I get an empty list.
getCurrentSession().createCriteria(WebElement.class)
.createCriteria("coreElements").add(
Restrictions.eq("value", value)).list();
But when executing the following HQL I get the correct result.
select distinct we from WebElement we, in(we.coreElements) core
where core.value = :inputValue
Can you help finding what am I doing wrong or different between those calls?(NOTE My preference is to work with the Criteria API instead of HQLs.
In your HQL you are creating an inner join which causes Hibernate to fetch the elements.
In the Criteria Query you can use setFetchMode() using FetchMode.JOIN
Since your query is static, I would recommend using HQL - it's easier to understand.
You are using a Restrictions.eq instead of Restrictions.in()..as you are using the HQL.
Related
I found similar questions, but they did not answer my question.
I have two entities with a many-to-one relationship - unidirectional.
But most importantly, the relationship is lazy. Because it is correct to use a lazy connection, everyone knows it.
Code:
#Entity
public class User implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
#Entity
public class City implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
}
interface BaseEntity {
void setId(Long id);
Long getId();
}
I wrote a method that allows you to search by the transferred fields of the entity.
An example of how this works:
public class Search<T extends BaseEntity> {
public List<T> getByFields(T entity, List<FieldHolder> data) {
// create criteria with passed field name and value by reflection
}
}
class FieldHolder {
private String fieldName;
private Object value;
/**
* "true" - means that the field "value" contains id related object
* "false" - elementary type like: String, Wrapper, Primitive
*/
private boolean isRelationId;
}
The problem is that problems start when you need to search and related objects - by creating related queries.
The following entry is used to send the associated field: "city.id" and the problem is that when I transfer the essence of the related object (City) it is in a proxy and I cannot get id by reflection from City.
My function works perfectly if you specify:
#ManyToOne(fetch = FetchType.EAGER)
private City city;
But it will greatly affect performance, since I have a lot of related objects. Therefore, I want to solve this problem for a lazy load.
I know that this is not an easy task. But perhaps there is some opportunity to somehow get around this problem.
I created a field annotated with the #ReadOnlyProperty so I can track a computed field value. However, when I make a normal findAll query, I receive a SQL Error warning that this column (hasChildren) does not exist. I already tried removing the GET method, but it does not solve the problem at all. I also tried using the #Transient annotation but I could not save the value on my property.
#Entity
#Table
public class Parent extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany( fetch = FetchType.LAZY)
private List<Child> childs;
// Computed column that come from #Query annotation
#ReadOnlyProperty
private boolean hasChildren;
//Getters
//Setters
}
Try annotating the field with JPAs #Transient. This should tell it not to persist it in the database.
I have an web application with hibernate which manages data in multiple languages. Currently basically every request generates a shower of select statements on the languagetranslations. The models are roughly as following:
Data <1-1> Placeholder <1-many> languageTranslation <many-1> language
If I query for all/many Dataobjects, I see lots of single selects which select one languageTranslation for the placeholder. The SQL I optimally would want to generate:
SELECT * FROM data join placeholder join languagetranslation
WHERE data.placeholder_id = placeholder.id
AND languagetranslation.placeholder_id = placeholder.id
AND languagetranslation.language_id = ?
so that I get every data with placeholder with translation in one single call. The languagetranslations have an composite primary key of language_id and placeholder_id.
I have no HBM file, everything is managed with annotations. Modelcode (only relevant sections are shown):
#Entity
public class Data {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, optional = false)
#Fetch(FetchMode.JOIN)
private Placeholder content;
}
public class Placeholder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "primaryKey.placeholder", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#Fetch(FetchMode.JOIN)
private Set<LanguageTranslation> languageTranslations = new HashSet<>();
}
public class LanguageTranslation {
#EmbeddedId
private LanguageTranslationPK primaryKey = new LanguageTranslationPK();
#Type(type = "org.hibernate.type.StringClobType")
private String text;
}
#Embeddable
public class LanguageTranslationPK {
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private TextPlaceholder textPlaceholder;
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private Language language;
}
public class Language {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
I experimented with FetchType and FetchMode but couldn't generate the behavior I want, it always single selects for single languageTranslations.
I also tried multiple ways to query, criteria based, HQL, and raw SQL. My current raw SQL query is the following:
String sql_query = "select data.*, lt.* from Data as data join languagetranslation as lt on data.content_id = lt.textplaceholder_id";
Query q = getSession().createSQLQuery(sql_query).addEntity("data", Data.class).addJoin("data.content_id", "data.title").addJoin("lt", "data.content.languageTranslations").setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return q.list();
Am I doing something generally wrong here? How can I convince hibernate to get all entities in one single database call? Or is there some other methods to improve performance in my case (e.g. batch selecting)?
You may create proxy pojo which have your all entity variables with getter setter and constructor. then initialize this constructor in hibernate query so that you just get all needed data from database.
import com.proxy;
class userProxy{
private string name;
private string password;
private string address;
private int pincode;
private byte[] profilePic;
private int age;
public userProxy(string name,string password){
this.name = name;
this.password = password;
}
//Getter and setter of all variable...
}
Then use this constructor to Hibernate query like
select new com.proxy.userProxy(user.name,user.password) from usertable
Am I doing something generally wrong here?
No, you are not. That is how Hibernate works.
How can I convince hibernate to get all entities in one single database call
You have to use HQL or SQL query to do that. You do not need to have HBM file. It can be done through #NamedQueries / #NamedQuery annotation with list method.
There are many samples on Internet as example simple one:
http://www.mkyong.com/hibernate/hibernate-named-query-examples/
I am working on a Hibernate issue, which involves 2 separate Entity beans defined separately in their own classes:
Store
StoreServer
Note that a Store will have more than one StoreServer - hence the use of the #OneToMany annotation. Please see the code snippets as follows:
Store:
#Entity
#Table(name="Store")
public class Store implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = 5644190852867691168L;
#Id
#Column(name="STORE_NO", nullable=false)
private int storeNumber;
#Column(name="STORE_NAME", nullable=false)
private String storeName;
#Column(name="STORE_PHONE", nullable=false)
private String storePhone;
//other Store fields...
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name="STORE_NO", insertable=false, updatable=false)
private List<StoreServer> storeServers = new ArrayList<StoreServer>();
//getters and setters
StoreServer:
#Entity
#Table(name="Store_Server")
public class StoreServer implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -5410564578856243437L;
#Id
private StoreServerPK storeServerPK;
#Column(name="IP_ADDRESS", nullable=true)
private String ipAddress;
//other StoreServer fields...getters and setters
Since StoreServer has a composite Primary Key, here is StoreServerPK:
#Embeddable
public class StoreServerPK implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -1401889029390423604L;
#Column(name="STORE_NO", nullable=false)
protected int storeNumber;
#Column(name="SERVER_NO", nullable=false)
protected String serverNumber;
//getters and setters
At present, I am getting the correct results, but the performance is unacceptably SLOW. I have switched on logging in Hibernate and I can see that a separate SELECT query is being run for each Store Entity in order to obtain the associated StoreServer records.
Currently, in the logs, I see a single SELECT statement to obtain the Store records (more than 200 results returned). Then for each store, a new SELECT statement to get the StoreServer records. My question is...Why is Hibernate not doing a join (running one query)?
Please could I get some help on how to tell Hibernate to run a single query, using a JOIN?
Thank you
It is called N+1 problem
The solution actually depends on how do you make your query - in case if you are using Criteria API you should use Root.fetch method:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Store> cq = qb.createQuery(Store.class);
Root<Store> root = cq.from(Store.class);
root.fetch(App_.storeServers, JoinType.LEFT);
cq.select(root);
return em.createQuery(cq).getResultList();
If you are using HQL you should use fetch keyword:
select distinct st from Store st left join fetch st.storeServers
It might be a good idea to validate the number of queries generated by Hibernate in your unit tests using in-memory database like H2 and JDBC Sniffer
I have a simple Java EE 7 Web App with Eclipselink and the TABLE_PER_CLASS inheritance strategy.
Following classes:
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Entity
public abstract class AbstractService implements Serializable {
private static final long serialVersionUID = 7041207658121699813L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn
private PersonGroup personGroup;
}
#Entity
public class Service extends AbstractService implements Serializable {
private static final long serialVersionUID = 3817106074951672799L;
}
#Entity
public class PersonGroup implements Serializable {
private static final long serialVersionUID = 3205092801888510996L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "personGroup")
private List<AbstractService> services;
}
In persistence.xml I do Drop&Create.
After creating the database, I have this tables:
abstractservice, service, persongroup
The point is now, that eclipselink creates the table abstractservice with (only(!)) the attribute persongroup_id (no "id" attribute). Why?
My understanding from TABLE_PER_CLASS is, that every attribute and key is going "down", so abstractservice should have no more attributes and should not exist.
My businesscase is, that I have a lot of subservice from AbstractService. I want to get all subservices from AbstractService with a special persongroup.
The AbstractServicetable has no entries, because everything is in Service.
With CriteriaBuilder I say:
Select from AbstractService where persongroup_id = 123;
The Criteria Api should build this (with some union, if more subservices would exist), because I have TABLE_PER_CLASS:
Select from Service where persongroup_id = 123;
Why is eclipselink creating persongroup_id in abstractService and how can I solve my case?
At the end the result of the query is always empty, because abstractService is empty...
Same question was asked here: http://www.eclipse.org/forums/index.php/t/406338/
and seems to be related to bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=265702 which was fixed but regressed. A new bug should be filed for it if you are seeing this in the latest version.
If you are only using a single Servide subclass, you might want to make it a mappedSuperclass instead. If not, a different inheritance type such as joined or single table is usually recommended. This bug seems to only affect DDL generation, so you can switch to have JPA create a script that you can then edit to remove the AbstractService table entries.