I got a question regarding hibernate.
I have to classes with many to many relationship and try to make a select. The problem is I am getting an exception. Can you help?
#Entity(name = "pracownik")
#Inheritance(strategy = InheritanceType.JOINED)
#Proxy(lazy = false)
public class Pracownik extends Osoba {
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
#JoinTable(name = "grafikpracownika", joinColumns = { #JoinColumn(name = "idosoby") },
inverseJoinColumns = { #JoinColumn(name = "idgrafiku") })
private List<Grafik> grafiki = new ArrayList<>();
}
The second entity
#Entity (name = "grafik")
#Proxy(lazy = false)
public class Grafik {
#ManyToMany (mappedBy = "grafiki",cascade = { CascadeType.ALL })
private List <Pracownik> pracownik = new ArrayList<>();
}
The method I developed is:
public List<Pracownik> getWszyscyPracownicyGrafiku() {
List<Pracownik> pracownicy = new ArrayList<>();
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from pracownik as p join p.grafiki");
pracownicy = query.list();
transaction.commit();
return pracownicy;
}
And the exception is:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to model.Pracownik
Any idea what is wrong?
I also would like to add a "where" but it should be easy after I get rid of this exception.
I also tried with "normal" sql
SELECT * from pracownik p join grafikpracownika g on p.idosoby = g.idosoby where idgrafiku = 6
but what I am getting is:
org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [idosoby] during auto-discovery of a native-sql query
You're selecting from two tables without explicit select clause, therefore Hibernate produces a list of tuples (Pracownik, Grafik) as a result.
If you want only Pracowniks (i.e. join is needed to create condition on p.grafiki in where), use
select distinct p from pracownik as p join p.grafiki
If join is used to instruct Hibernate to fetch associated Grafiks, use join fetch:
from pracownik as p join fetch p.grafiki
Related
In my spring boot application, I have two entities: ScheduledLegEntity and BookingLegEntity.
#Entity
#Table(name="SCHEDULED_LEG")
public class ScheduledLegEntity {
// ...
#OneToMany(mappedBy = "getOn", cascade = CascadeType.MERGE)
private List<BookingLegEntity> enteringBookingLegs;
#OneToMany(mappedBy = "getOff", cascade = CascadeType.MERGE)
private List<BookingLegEntity> exitingBookingLegs;
// ...
}
#Entity
#Table(name = "BOOKING_LEG")
public class BookingLegEntity {
// ...
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "GET_ON")
private ScheduledLegEntity getOn;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "GET_OFF")
private ScheduledLegEntity getOff;
// ...
}
Now, I need to query my database for a specific set of scheduled legs and I also need to work with their enteringBookingLegs and exitingBookingLegs. I quickly stumbled accross this exception:
rg.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.mopla.server.schedule.entities.ScheduledLegEntity.enteringBookingLegs, could not initialize proxy - no Session
I tried solving this (and the subsequent MultipleBagFetchException) problem by following this guide on baeldung ("8. Ideal Solution: Using Multiple Queries
") like this:
public class CustomScheduledLegRepositoryImpl implements CustomScheduledLegRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<ScheduledLegEntity> customFindMethod(){
List<ScheduledLegEntity> legs = entityManager.createQuery(
"select distinct p from ScheduledLegEntity p left join fetch p.enteringBookingLegs", ScheduledLegEntity.class)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
legs = entityManager.createQuery("select distinct p from ScheduledLegEntity p left join fetch p.exitingBookingLegs t where p in :legs", ScheduledLegEntity.class)
.setParameter("legs", legs)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
return legs;
}
}
But even with those two queries, only either enteringBookingLegs or exitingBookingLegs are empty (see screenshots from debugging). How can I solve this problem (efficiently)?
As you can see, only one of the lists can be accessed, first exitingBookingLegs and then enteringBookingLegs, but never both at the same time.
I have following model:
#Entity
#Table(name = "SAMPLE_TABLE")
#Audited
public class SampleModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME", nullable = false)
#NotEmpty
private String name;
#Column(name = "SHORT_NAME", nullable = true)
private String shortName;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "MENTOR_ID")
private User mentor;
//other fields here
//omitted getters/setters
}
Now I would like to query only columns: id, name, shortName and mentor which referes to User entity (not complete entity, because it has many other properties and I would like to have best performance).
When I write query:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<SampleModel> query = builder.createQuery(SampleModel.class);
Root<SampleModel> root = query.from(SampleModel.class);
query.select(root).distinct(true);
root.fetch(SampleModel_.mentor, JoinType.LEFT);
query.multiselect(root.get(SampleModel_.id), root.get(SampleModel_.name), root.get(SampleModel_.shortName), root.get(SampleModel_.mentor));
query.orderBy(builder.asc(root.get(SampleModel_.name)));
TypedQuery<SampleModel> allQuery = em.createQuery(query);
return allQuery.getResultList();
I have following exception:
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.sample.SampleModel.model.SampleModel.mentor,tableName=USER_,tableAlias=user1_,origin=SampleModel SampleModel0_,columns={SampleModel0_.MENTOR_ID ,className=com.sample.credential.model.User}}]
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:214)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:991)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:759)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:675)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:311)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:259)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)
... 138 more
Query before exception:
SELECT DISTINCT NEW com.sample.SampleModel.model.SampleModel(generatedAlias0.id, generatedAlias0.name, generatedAlias0.shortName, generatedAlias0.mentor)
FROM com.sample.SampleModel.model.SampleModel AS generatedAlias0
LEFT JOIN FETCH generatedAlias0.mentor AS generatedAlias1
ORDER BY generatedAlias0.name ASC
I know that I can replace fetch with join but then I will have N+1 problem. Also I do not have back reference from User to SampleModel and I do not want to have..
I ran into this same issue, and found that I was able to work around it by using:
CriteriaQuery<Tuple> crit = builder.createTupleQuery();
instead of
CriteriaQuery<X> crit = builder.createQuery(X.class);
A little extra work has to be done to produce the end result, e.g. in your case:
return allQuery.getResultList().stream()
map(tuple -> {
return new SampleModel(tuple.get(0, ...), ...));
})
.collect(toList());
It's been a long time since the question was asked. But I wish some other guys would benefit from my solution:
The trick is to use subquery.
Let's assume you have Applicant in your Application entity (one-to-one):
#Entity
public class Application {
private long id;
private Date date;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "some_id")
private Applicant applicant;
// Other fields
public Application() {}
public Application(long id, Date date, Applicant applicant) {
// Setters
}
}
//...............
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Application> cbQuery = cb.createQuery(Application.class);
Root<Application> root = cbQuery.from(Application.class);
Subquery<Applicant> subquery = cbQuery.subquery(Applicant.class);
Root subRoot = subquery.from(Applicant.class);
subquery.select(subRoot).where(cb.equal(root.get("applicant"), subRoot));
cbQuery.multiselect(root.get("id"), root.get("date"), subquery.getSelection());
This code will generate a select statement for Application, and select statements for Applicant per each Application.
Note that you have to define an appropriate constructor corresponding to your multiselect.
I got the same problem using EclipseLink as the JPA provider : I just wanted to return the id of a mapped entity («User» in Gazeciarz's example).
This can be achieved quite simply by replacing (in the query.multiselect clause)
root.get(SampleModel_.mentor)
with something like
root.get(SampleModel_.mentor).get(User_.id)
Then, instead of returning all the fields of User, the request will only return the its id.
I also used a tuple query but, in my case, it was because my query was returning fileds from more than one entity.
I have 2 entities with #Where annotation. First one is Category;
#Where(clause = "DELETED = '0'")
public class Category extends AbstractEntity
and it has the following relation;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "category")
private Set<SubCategory> subCategories = Sets.newHashSet();
and second entity is SubCategory;
#Where(clause = "DELETED = '0'")
public class SubCategory extends AbstractEntity
and contains corresponding relation;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
Whenever I call the following Dao method;
#Query(value = "select distinct category from Category category join fetch category.subCategories subcategories")
public List<Category> findAllCategories();
I got the following sql query;
select
distinct category0_.id as id1_3_0_,
subcategor1_.id as id1_16_1_,
category0_.create_time as create2_3_0_,
category0_.create_user as create3_3_0_,
category0_.create_userip as create4_3_0_,
category0_.deleted as deleted5_3_0_,
category0_.update_time as update6_3_0_,
category0_.update_user as update7_3_0_,
category0_.update_userip as update8_3_0_,
category0_.version as version9_3_0_,
category0_.name as name10_3_0_,
subcategor1_.create_time as create2_16_1_,
subcategor1_.create_user as create3_16_1_,
subcategor1_.create_userip as create4_16_1_,
subcategor1_.deleted as deleted5_16_1_,
subcategor1_.update_time as update6_16_1_,
subcategor1_.update_user as update7_16_1_,
subcategor1_.update_userip as update8_16_1_,
subcategor1_.version as version9_16_1_,
subcategor1_.category_id as categor11_16_1_,
subcategor1_.name as name10_16_1_,
subcategor1_.category_id as categor11_3_0__,
subcategor1_.id as id1_16_0__
from
PUBLIC.t_category category0_
inner join
PUBLIC.t_sub_category subcategor1_
on category0_.id=subcategor1_.category_id
where
(
category0_.DELETED = '0'
)
Could you please tell me why the above query lacks
and subcategor1_.DELETED = '0'
inside its where block?
I have just solved a similar problem in my project.
It is possible to put #Where annotation not only on Entity, but on also on your child collection.
According to the javadoc:
Where clause to add to the element Entity or target entity of a collection
In your case, it would be like :
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "category")
#Where(clause = "DELETED = '0'")
private Set<SubCategory> subCategories = Sets.newHashSet();
Please find a similar issues resolved here
I believe thus solution is not as invasive compared to using Hibernate Filters.These filters are disabled by default and operate on Session level, thus enabling them each time new Session opens is extra work especially when your DAO works through abstractions like Spring Data
This is a quick reply;
#Where(clause = "DELETED = '0'")
public class SubCategory extends AbstractEntity
Where will effect when direct query for SubCategry.
To not get deleted sub categories use Hibernate Filters
as exampled on here
I have a bidirectional one-to-many relation between this entities, all of this was created by the JPA wizard of Netbeans
Vehiculo (Vehicle)
#Entity
#Table(name = "vehiculo")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Vehiculo.findAll", query = "SELECT v FROM Vehiculo v"),
#NamedQuery(name = "Vehiculo.findById", query = "SELECT v FROM Vehiculo v WHERE v.id = :id")})
public class Vehiculo implements Serializable {
//more attributes
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idVehiculo", fetch = FetchType.LAZY)
private List<PuntoTrayecto> puntoTrayectoList;
//methods
}
PuntoTrayecto (TrayectoryPoint)
#Entity
#Table(name = "punto_trayecto")
#XmlRootElement
public class PuntoTrayecto implements Serializable {
//more attributes
#JoinColumn(name = "id_vehiculo", referencedColumnName = "id")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Vehiculo idVehiculo;
}
I need to get a "Vehiculo" by the id, so I call the NamedQuery "Vehiculo.findById"
Query query = em.createNamedQuery("Vehiculo.findById");
query.setParameter("id", 1);
Object o = query.getSingleResult();
Vehiculo vehiculo = (Vehiculo)o;
I get this exception:
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'precision, tiempo, velocidad, id_vehiculo FROM punto_trayecto WHERE (id_vehiculo' at line 1
And this is the query that is trying to call
Call: SELECT id, latitud, longitud, precision, tiempo, velocidad, id_vehiculo FROM punto_trayecto WHERE (id_vehiculo = ?)
It corresponds to the other entity "PuntoTrayecto" and I guess this SELECT query is call to fill the List puntoTrayectoList of the "Vehiculo", and the same happens if I use the JPA Controller. What's wrong?
The problem is probably caused by the column precision. This is a reserved word (according to MySQL). You can check this answer for a workaround
I have two entities:
#Entity
#Table(name = "ACCOUNT")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class MyCloudAccount implements Serializable {
...
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<ServerInstance> servers = new HashSet<ServerInstance>();
...
}
#Entity
#Table(name = "SERVER_INSTANCE")
public class ServerInstance implements Serializable {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ACCOUNT_ID")
private MyCloudAccount account;
...
}
I am getting all accounts by this code:
StringBuilder sql = new StringBuilder();
sql.append("SELECT e FROM ");
sql.append(persistentClass.getName());
sql.append(" e");
return entityManager.createQuery(sql.toString()).getResultList();
And this produces one query for the account and N queries for the servers instead of one with outer join. How to force JPA to make the query in optimal way?
I find it more convenient to use Java Persistence Query Language
you can do:
#NamedQueries{
#NamedQuery(name="myQuery" query="SELECT a FROM MyCloudAccount JOIN FETCH a.servers")
}
public class MyCloudAccount{
...
}
then you can do
TypedQuery<MyCloudAccount> query = em.createNamedQuery("MyCloudAccount.myQuery", MyCloudAccount.class);
List<MyCloudAccount> results = query.getResultList();
EDIT
You are actually already using JPQL. The key thing to your problem is using the JOIN FECTH command.