I don't now why occur N+1 Problem using QueryDSL - java

I used Spring Boot and QueryDSL.
When called findAllByWriterGroupByClient method in ClientMemoRepositoryImpl.java, [generated query 1] generated once and [generated query 2] generated several times.
Additionally, when read result of this query as Tuple in ClientMemoServiceImpl.java, [generated query 3] is generated many times.
ClientMemoRepositoryImpl.java
#Override
public List<Tuple> findAllByWriterGroupByClient(String searchKeyword, Long writerId, boolean hasAdminRole) {
QClientMemo qClientMemo1 = new QClientMemo("cm1");
QClientMemo qClientMemo2 = new QClientMemo("cm2");
JPAQuery<Tuple> memoDtoJPAQuery = qf.select(
JPAExpressions.selectFrom(qClientMemo1)
.where(qClientMemo1.clientId.eq(qClientMemo.clientId).and(
qClientMemo1.createdDate.eq(
JPAExpressions
.select(qClientMemo2.createdDate.max())
.from(qClientMemo2)
.where(qClientMemo2.clientId.eq(qClientMemo.clientId))
)
)
),
new CaseBuilder()
.when(qClientMemo.createdDate.gt(LocalDateTime.now().minusDays(7)))
.then(1)
.otherwise(0).sum()
)
.from(qClientMemo);
if ((!hasAdminRole) && writerId != null) {
memoDtoJPAQuery = memoDtoJPAQuery.where(qClientMemo.writer.id.eq(writerId));
}
if (searchKeyword != null)
memoDtoJPAQuery = memoDtoJPAQuery.where(
qClientMemo.title.contains(searchKeyword)
.or(qClientMemo.content.contains(searchKeyword))
.or(qClientMemo.clientId.clientName.contains(searchKeyword))
.or(qClientMemo.writer.name.contains(searchKeyword))
);
return memoDtoJPAQuery
.groupBy(qClientMemo.clientId)
.orderBy(OrderByNull.DEFAULT)
.fetch();
}
generated query 1
select
(select
clientmemo1_.id
from
client_memo clientmemo1_
where
clientmemo1_.client_id=clientmemo0_.client_id
and clientmemo1_.created_date=(
select
max(clientmemo2_.created_date)
from
client_memo clientmemo2_
where
clientmemo2_.client_id=clientmemo0_.client_id
)
) as col_0_0_, sum(case
when clientmemo0_.created_date>? then ?
else 0
end) as col_1_0_
from
client_memo clientmemo0_
group by
clientmemo0_.client_id
order by
null asc
generated query 2
select
[all fields of client_memo entity]
from
client_memo clientmemo0_
where
clientmemo0_.id=?
generated query 3
select
[all fields of client entity]
from
client client0_
where
client0_.id=?
ClientMemoServiceImpl.java
List<Tuple> clientMemos = clientMemoRepository.findAllByWriterGroupByClient(
readClientMemoDto.getSearchKeyword(),
readClientMemoDto.getUserId(),
hasAdminRole
);
clientMemos.forEach(clientMemo -> {
Map<String, Object> result = new HashMap<>();
Integer newCnt = clientMemo.get(1, Integer.class);
if (newCnt != null) {
result.put("newMemoNum", newCnt);
}
MemoDto memoDto = new MemoDto();
ClientMemo memo = clientMemo.get(0, ClientMemo.class);
if (memo != null) {
memoDto.ofClientMemo(memo);
result.put("memoDetail", memoDto);
}
results.add(result);
});
ClientMemo.java
#Entity
#Table(name = "client_memo")
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#DynamicInsert
public class ClientMemo {
#JsonIgnore
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "title", nullable = false)
private String title;
#Lob
#Column(name = "content")
private String content;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="client_id")
private Client clientId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="writer")
private User writer;
#Column(name = "created_date")
private LocalDateTime createdDate;
#Column(name = "updated_date")
private LocalDateTime updatedDate;
#Column(name = "is_admin")
private boolean isAdmin;
}
Client.java
#Entity
#Table(name = "client")
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#DynamicInsert
public class Client {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "client_name", nullable = false)
private String clientName;
#Column(name = "client_phone_num", nullable = false)
private String clientPhoneNum;
#Column(name = "service_start_time")
private LocalDateTime serviceStartTime;
#Column(name = "service_end_time")
private LocalDateTime serviceEndTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "media_id")
private Media media;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_charge_id")
private User personCharge;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "normal_memo")
private ClientMemo normalMemo;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "admin_memo")
private ClientMemo adminMemo;
#Column(name = "status", columnDefinition = "varchar(20) default 'UNCONTRACTED'")
#Enumerated(EnumType.STRING)
private ClientStatus status;
#Column(name = "is_deleted", nullable = false)
private boolean isDeleted;
}
All FetchType of Data Relationship are FetchType.LAZY.
I don't understand why occur this problem and why some people say that better using FetchType.LAZY than FetchType.EAGER.
Do I understand QueryDSL or SpringBoot correctly?
Thanks

You should share with us your Jpa entities.
In my opinion, you should have setted some associations in your entity (with #OneToMany, etc..), probably in Eager Mode (which is the default mode).
When you try to load one instance of your object from the database, Hibernate loads the associations as well. In eager mode, hibernate loads the associations by querying the database (which generates additional sql queries).
If you define your associations in Lazy mode, Hibernate will populate your entity jpa with some proxy objects and will fetch the associations later, only when you access it (so that means the sql query of your association is deffered when you only try to access the association in your code).

Related

JPA/Hibernate ManyToOne Association always null

I have two entities BookingLegEntity and BookingEntity which reference each other. But anytime I try to retrieve them from the database (e.g. via findByUuid), BookingLegEntity.belongsTo remains null.
Here are my entities:
#Entity
#Table(name = "BOOKING_LEG")
#SQLDelete(sql = "UPDATE BOOKING_LEG SET deleted = true WHERE id=?")
#Where(clause = "deleted=false")
public class BookingLegEntity {
#Id
#Column(name = "ID", unique = true, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "UUID", nullable = false)
private UUID uuid;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "BELONGS_TO")
private BookingEntity belongsTo;
// ..
#ManyToOne
#JoinColumn(name = "DISTRIBUTOR")
private DistributorEntity distributor;
#Column(name = "TRANSPORT_TYPE")
#Convert(converter = TripTypeEnumConverter.class)
private TripTypeEnum transportType;
// ...
}
#Entity
#Table(name="BOOKINGS")
#SQLDelete(sql = "UPDATE BOOKINGS SET deleted = true WHERE id=?")
#Where(clause = "deleted=false")
public class BookingEntity {
#Id
#Column(name="ID", unique=true, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="BOOKING_ID")
#Convert(converter = BookingIdConverter.class)
private BookingId bookingId;
#ManyToOne
#JoinColumn(name ="BOOKED_BY")
private UserEntity bookedBy;
// ..
#OneToMany(mappedBy = "belongsTo", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<BookingLegEntity> bookingLegs = new HashSet<>();
// ...
}
Here is my repository:
#Repository
public interface BookingLegRepository extends JpaRepository<BookingLegEntity, Long> {
Optional<BookingLegEntity> findByUuid(UUID id);
// ...
}
The values in the database itself look correct:
What is really strange is that this has worked before (belongsTo was not null) but suddenly stopped working. Does anyone has any idea as to what we might do wrong here?
Do not use cascade = CASCADEType.ALL on your ManyToOne annotation, because removing one BookingLeg will cause a removal of all in corresponding Booking
The solution should be to use
cascade = CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}) in its stead.
I would Truncate Cascade or Delete from Bookings where original_itinerary is null before i move on to using the new entities.
Sincerely hope it helps. (No hate if it doesn't pls)
Edit : i didnt see that comment by #dey, its my own. :P saw his comment after posting my ans

Spring boot JPA related entity clarification

I have an entity like Process, which will be created by , updated by one user. When I try to apply the filter. I have created the foreign key relationship in the database. Now, when I use the JPA Specification to apply dynamic filter, I am getting exception as
No property CREATED found for type Process!
#Table(name = "process")
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROCESS_ID")
#JsonProperty("id")
private Long id = null;
#NotNull
#Column(name = "NAME")
#JsonProperty("name")
private String name = null;
#Column(name = "CREATED_BY", updatable = false)
#JsonProperty("createdBy")
private Long createdBy = null;
#Column(name = "updatedBy", nullable = true)
#JsonProperty("updatedBy")
private Long updatedBy = null;
}
Hence, I Added the entity relationship mapping in the process entity as given below,
Now, I am getting below error. I am new to JPA and hibernate, the relation mapping is very confusing, kindly help.
#Table(name = "process")
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROCESS_ID")
#JsonProperty("id")
private Long id = null;
#NotNull
#Column(name = "NAME")
#JsonProperty("name")
private String name = null;
#Column(name = "CREATED_BY", updatable = false)
#JsonProperty("createdBy")
private Long createdBy = null;
#Column(name = "updatedBy", nullable = true)
#JsonProperty("updatedBy")
private Long updatedBy = null;
//newly added below properties so that there will be no error when fetching data
#OneToOne(targetEntity = UserDetails.class, fetch = FetchType.LAZY, mappedBy = "id")
private UserDetails CREATED;
#OneToOne(targetEntity = UserDetails.class, fetch = FetchType.LAZY, mappedBy = "id")
private UserDetails UPDATED;
}
Now, I am getting the below error
Referenced property not a (One|Many)ToOne: com.app.users.details.domain.UserDetails.id in mappedBy of com.app.scenarios.domain.Process.CREATED
Kindly let me know what i am doing wrong. I have a process which can be created by a user and can be updated by a user. In DB, I am having a foreign key relationship for process and userdetails entity.
EDIT
Code to get the filtered data from DB using JPA Specification
Page<process> result = this.processDao.findAll(getprocessGridData(processSearchCondition.getprocessName()), pageRequest);
private static Specification<process> getprocessGridData(String processName) {
return (Specification<process>) (root, query, criteriaBuilder) -> (
criteriaBuilder.like(root.get("name"), processName)
);
}
I guess what you actually want is this:
#Table(name = "process")
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROCESS_ID")
#JsonProperty("id")
private Long id;
#NotNull
#Column(name = "NAME")
#JsonProperty("name")
private String name;
#OneToOne(fetch = FetchType.LAZY)
#jOINColumn(name = "CREATED_BY", updatable = false)
private UserDetails createdBy;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "UPDATED_BY", nullable = true)
private UserDetails updatedBy;
}

Hibernate filter apply every time entity is loaded

I am trying to write a multilanguage software. Using filters in hibernate, I want that every time an entity is accessed, only translation with the specific language being load.
#Entity
#Table(name = "province")
#FilterDef(name = "findByLanguage", parameters = {#ParamDef(name = "language", type = "string")})
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "country_id", nullable = false)
private Country country;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "province_id")
#Filter(name = "findByLanguage", condition = "language = :language")
private Set<ProvinceTranslation> translations;
}
This works as I expected for province, But Country has also a list of translations, and I want that CountryTranslation list is also contains only specified language when I load a province. But this code does not work:
#Entity
#Table(name = "country")
#FilterDef(name = "findByLanguage", parameters = {#ParamDef(name = "language", type = "string")})
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "country_id")
private List<Province> provinces;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "country_id")
#Filter(name = "findByLanguage", condition = "language = :language")
private Set<CountryTranslation> translations;
}
So if I load Country directly everything is ok, But if I load Province and access Country inside it, loading Country is different and #Filter is being ignored. How can I achieve this?

Hibernate unexpected deletes when finding entities

We have a Java ee application running on JBoss 6.4 GA using JPA and Hibernate with the following entities:
#Entity
#SequenceGenerator(name = "sequence", sequenceName="SEQ_CAMPAIGNS_ID",allocationSize = 1)
#Table(name = "CAMPAIGN")
public class CampaignEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "IS_ACTIVE", nullable = false)
private boolean active;
#Column(name = "START_DATE", nullable = false)
private Date startDate;
#Column(name = "END_DATE", nullable = false)
private Date endDate;
#Column(name = "LEGAL_ENTITY_ID", nullable = false)
private Integer legalEntityId;
#Column(name = "DEPARTMENT", nullable = false)
#Enumerated(value = EnumType.STRING)
private Department department;
#Column(name = "CATEGORY", nullable = false)
#Enumerated(value = EnumType.STRING)
private Category category;
#Embedded
CampaignConditionsEntity campaignConditions;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "campaign", orphanRemoval = true)
#OrderBy
private List<CodeEntity> campaignCodes;
public CampaignEntity() {
}
And the following CampaignConditionsEntity:
#Embeddable
public class CampaignConditionsEntity implements Serializable {
private static final String CAMPAIGN_ID = "CAMPAIGN_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAMPAIGN_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CAMPAIGN_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
And the following CodeEntity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#SequenceGenerator(name = "sequence", sequenceName = "SEQ_CODES_ID", allocationSize = 1)
public abstract class CodeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAMPAIGN_ID")
private CampaignEntity campaign;
#OneToOne(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private DiscountEntity discount;
#Column(name = "MAX_USAGES", nullable = false)
private Integer maxUsages;
#Column(name = "UNLIMITED_USAGES", nullable = false)
private boolean unlimitedUsages;
#Column(name = "NEGATIVE_SH", nullable = false)
private boolean negativeSH;
#Column(name = "UNIQUE_BUYER", nullable = false)
private boolean uniqueBuyer;
#Column(name = "START_DATE")
private Date startDate;
#Column(name = "END_DATE")
private Date endDate;
#Embedded
private CodeConditionsEntity codeConditions;
public CodeEntity() {
}
This is the CodeConditionsEntity:
#Embeddable
public class CodeConditionsEntity implements Serializable {
private static final String CODE_ID = "CODE_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CODE_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_CARRIERS", joinColumns = #JoinColumn(name = CODE_ID))
private Set<CarrierConditionEntity> carrierConditions;
This is the CarrierConditionEntity:
#Embeddable
public class CarrierConditionEntity implements Serializable {
#Column(name = "CARRIER", nullable = false, length = 3)
private String carrierCode;
#Column(name = "IS_INCLUDED", nullable = false)
private boolean included;
The problem is that in the logs we are finding unexpected deletes when the only operation that we are doing are finds of particular campaign entities.
In the production logs we find the following deletes
Hibernate: delete from CODE_COND_CARRIERS where CODE_ID=? and CARRIER=? and IS_INCLUDED=?
do you have any suggestion?
thanks
I have some suggestions :)
Be aware of what is a Persistence Context (EntityManager instance in JPA terminology / Session in Hibernate one), the entity lifecycle and transaction scope (unit of work)
Do not mutate entity state if you don't expect the changes to be reflected in database, or at least detach the entity before mutating it.
Mark your transaction as "readOnly" if you only fetch data in the related unit of work. (beware that if you have many "Transactional" methods joining the same physical transaction, the flag is set by the surrounding one and cannot be overridden by inner logical transactions). That way the EntityManager won't be flushed at the end of the transaction and pending changes won't be persisted to the database.
You can track the method triggering the unexpected deletion using an EntityListener on the related entity and printing the current strackTrace (new Throwable().printStackTrace()/ log(new Throwable()) in the PreRemove method
I found where was the problem:
The problem was that the Entities didn't have the equals() and the hashcode() implemented. Also there were entities that have a #PostLoad that modified the entity after loading it from database. Then in this situation Hibernate though that there was a change in those entities that didn't have the equals and the hashcode, and then it delete all of them and inserted again in the database (to have the same entities before the query)
Adding the equals and hashcode methods and deleting postload removed the unexpected deletes and inserts from the logs.
regards

Hibernate One To Many criteria doesn't work

I have the following entities:
#Entity
#Table(name = "author")
public class Author implements Serializable {
private static final long serialVersionUID = 12345L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "author_id")
private int authorId;
#Column(name = "author_bio")
private String authorBio;
#Column(name = "author_email")
private String authorEmail;
#Column(name = "author_favourite_section")
private String authorFavouriteSection;
#Column(name = "author_password")
private String authorPassword;
#Column(name = "author_username")
private String authorUsername;
#OneToOne(mappedBy = "author", fetch = FetchType.LAZY)
private Blog blog;
#OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Post> posts;
// getters and setters
#Entity
#Table(name = "blog")
public class Blog implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "blog_id")
private int blogId;
#Column(name = "blog_title")
private String blogTitle;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "blog_author_id", unique = true)
private Author author;
#OneToMany(mappedBy = "blog", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Post> posts;
// getters and setters
#Entity
#Table(name = "post")
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "post_id")
private int postId;
#Column(name = "post_subject")
private String postSubject;
#Column(name = "post_body")
private String postBody;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "blog_id")
private Blog blog;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "post_author_id")
private Author author;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "post_tag", joinColumns = {
#JoinColumn(name = "post_id", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "tag_id",
nullable = false, updatable = false)})
private Set<Tag> tags = new HashSet<Tag>();
// getters and setters
#Entity
#Table(name = "tag")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tag_id")
private int tagId;
#Column(name = "tag_name")
private String tagName;
#ManyToMany(mappedBy = "tags", fetch = FetchType.LAZY)
private Set<Post> posts = new HashSet<Post>();
// getters and setters
The following data is presented in db:
author-blog-post-tag-AND-post_tag-tables
THE MAIN GOAL TO ACHIEVE IS: Find all authors which have written posts that contain appropriate tags.
I can do it using a SQL query:
SELECT a.author_id, a.author_bio, p.post_id, p.post_subject, t.tag_id, t.tag_name from author a
join blog b
on a.author_id = b.blog_author_id
join post p
on p.post_author_id = a.author_id
join post_tag pt
on p.post_id = pt.post_id
join tag t
on t.tag_id = pt.tag_id
where t.tag_name in ('Football', 'Basketball')
And the correct result is returned with author, filtered posts and tags.
But I need to do it using hibernate.
So using hibernate I want to find all authors which have written posts that contain appropriate tags.
And all those authors with ONLY those posts which contain indicated tags (see above - 'Football', 'Basketball') have to be returned.
I wrote this code:
final DetachedCriteria authorCriteria = DetachedCriteria.forClass(Author.class, "author");
authorCriteria.createAlias("author.posts", "post");
authorCriteria.createAlias("post.tags", "tag");
Criterion football = Restrictions.eq("tag.tagName", "Football");
Criterion basketball = Restrictions.eq("tag.tagName", "Basketball");
authorCriteria.add(Restrictions.or(football, basketball));
authorCriteria
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
final List<Author> result = (List<Author>)getConfiguredHibernateTemplate().findByCriteria(authorCriteria);
and I expected to receive:
Author(author_id = 54) with only one Post (post_id = 26) and this post containing two tags ('Football' and 'Basketball') as I received it using above SQL query.
But the actual result is that I receive Author(author_id = 54) with ALL HIS POSTS PRESENTED IN DB (ERROR AND PROBLEM HERE !!!) and each post contains all tags which also presented in db.
intellij-idea-debug-result
Hibernate generated the following queries:
select this_.author_id as author_i1_0_2_, this_.author_bio as author_b2_0_2_, this_.author_email as author_e3_0_2_, this_.author_favourite_section as author_f4_0_2_, this_.author_password as author_p5_0_2_, this_.author_username as author_u6_0_2_, post1_.post_id as post_id1_2_0_, post1_.post_author_id as post_aut4_2_0_, post1_.blog_id as blog_id5_2_0_, post1_.post_body as post_bod2_2_0_, post1_.post_subject as post_sub3_2_0_, tags5_.post_id as post_id1_2_, tag2_.tag_id as tag_id2_3_, tag2_.tag_id as tag_id1_4_1_, tag2_.tag_name as tag_name2_4_1_ from author this_ inner join post post1_ on this_.author_id=post1_.post_author_id inner join post_tag tags5_ on post1_.post_id=tags5_.post_id inner join tag tag2_ on tags5_.tag_id=tag2_.tag_id where (tag2_.tag_name=? or tag2_.tag_name=?)
select blog0_.blog_id as blog_id1_1_0_, blog0_.blog_author_id as blog_aut3_1_0_, blog0_.blog_title as blog_tit2_1_0_ from blog blog0_ where blog0_.blog_author_id=?
select posts0_.post_author_id as post_aut4_0_0_, posts0_.post_id as post_id1_2_0_, posts0_.post_id as post_id1_2_1_, posts0_.post_author_id as post_aut4_2_1_, posts0_.blog_id as blog_id5_2_1_, posts0_.post_body as post_bod2_2_1_, posts0_.post_subject as post_sub3_2_1_ from post posts0_ where posts0_.post_author_id=?
How do I achieve the expected and correctly filtered result using hibernate?
You asked for an author which writes a blog about Football or BaketBall:
DetachedCriteria.forClass(Author.class, "author");
It happens that this author also wrote blogs about something else. So you get what you've asked for. In your sql statement you ask for a projection whereas with hibernate you ask the ORM to get the object (author) with its posts collection.
I tried using projection ( authorCriteria.setResultTransformer(CriteriaSpecification.PROJECTION )
final DetachedCriteria authorCriteria = DetachedCriteria.forClass(Author.class, "author");
authorCriteria.createAlias("author.posts", "post");
authorCriteria.createAlias("post.tags", "tag");
final Criterion football = Restrictions.eq("tag.tagName", "Football");
final Criterion basketball = Restrictions.eq("tag.tagName", "Basketball");
authorCriteria.add(Restrictions.or(football, basketball));
authorCriteria.setResultTransformer(CriteriaSpecification.PROJECTION);
final List<Author> result = (List<Author>) getConfiguredHibernateTemplate().findByCriteria(authorCriteria);
and the following result I see in debugger:
Ok that is correct and I can analyze those data and sorted them to Author->List of Posts -> with Tags to specific Post. But this is extra work in code.
I assume maybe hibernate has more elegant way to return filtered data I need or NOT. If no way then I became disappointed of hibernate. Then it is more convenient to use some spring jdbc template or mybatis or something like that.
Using approach "You could then select Post as your root entity" involves extra queries to db. Why we need to do so extra work ? Looks like hibernate is not flexible and useful suffice if it can not run query which I want and return results in way I prefer.

Categories