Reputation: 31
While testing implementation of JPA into Spring I found out that my query is querying twice instead of once.
@Data
@Entity
@Table(name = "superfan_star")
public class Star implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(nullable = false)
private int id;
private String name;
private String nickname;
private String description;
private String thumbnail;
private String backgroundImage;
private Date created;
private Date updated;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
private Set<Media> medias;
}
This is model class.
@Service
public class SuperfanStarService
{
@Autowired
private StarRepository starRepository;
@PersistenceContext
private EntityManager em;
@Transactional
public List<Star> getStars()
{
QStar qStar = QStar.star;
QMedia qMedia = QMedia.media;
List<Star> stars =
new JPAQuery(em)
.from(qStar)
.where(qStar.id.eq(19))
.list(qStar);
return stars;
}
}
This is my service class.
20160915 20:52:59.119 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82) 9. select star0_.id as id1_2_, star0_.background_image as backgrou2_2_, star0_.created as created3_2_, star0_.description as descript4_2_, star0_.name as name5_2_, star0_.nickname as nickname6_2_, star0_.thumbnail as thumbnai7_2_, star0_.updated as updated8_2_ from superfan_star star0_ inner join superfan_media medias1_ on star0_.id=medias1_.star_id where star0_.id=19
20160915 20:52:59.173 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82) 9. select medias0_.star_id as star_id11_2_0_, medias0_.id as id1_1_0_, medias0_.id as id1_1_1_, medias0_.created as created2_1_1_, medias0_.description as descript3_1_1_, medias0_.end_time as end_time4_1_1_, medias0_.is_approve as is_appro5_1_1_, medias0_.is_approved_final as is_appro6_1_1_, medias0_.is_pushed as is_pushe7_1_1_, medias0_.is_represent as is_repre8_1_1_, medias0_.length as length9_1_1_, medias0_.released as release10_1_1_, medias0_.star_id as star_id11_1_1_, medias0_.teleport_media_id as telepor12_1_1_, medias0_.thumbnail as thumbna13_1_1_, medias0_.title as title14_1_1_, medias0_.work_end as work_en15_1_1_, medias0_.work_start as work_st16_1_1_, medias0_.youtube_id as youtube17_1_1_, medias0_.youtube_title as youtube18_1_1_ from superfan_media medias0_ where medias0_.star_id=19
As you can see, it's querying twice instead of once, probably because of inverse update? Is there any way to make my JPA model query only once?
Upvotes: 1
Views: 2808
Reputation: 23226
While there is an accepted answer I suspect there maybe something else at play here. I note you have a Lombok @Data
which I believe overrides equals()
and hashcode()
based on all fields which is dangerous in a JPA entity as it can trigger lots of additional data being loaded when associated items are added to hash based collections.
Yeah I found out that Lombok is causing problems for lists as it's querying medias for each Star. I'm trying to see if there's a way to use Lombok without querying everything but there doesn't seem to be a way.
Firstly, I would suggest not implementing equals()
and hashcode()
based on all fields of your entity: that is the root cause of your problem and makes no sense anyway - base them on a unique business key if you have one available. Essentially two entities are equal if they have the same id but see here however:
The JPA hashCode() / equals() dilemma.
Additionally, hashcode() should be based on immutable fields - see here:
http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/.
Lombok's @Data
just aggregates other individual annotations. So you can remove it, use the individual @Getter
@Setter
and @ToString
Lombok annotations and write your own sensible implementations of equals() and hashcode() when required:
https://projectlombok.org/features/Data.html
Upvotes: 1
Reputation: 206816
This works as expected. The first query gets the Star
entity with id = 19 from the database, and the second query gets the linked Media
entities for that Star
entity from the database. (Carefully look at the log of the SQL statements to understand what is being queried).
Note that you specified FetchType.EAGER
on the medias
field in class Star
:
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
private Set<Media> medias;
Eager fetching means that when you do a query for one or more Star
objects, Hibernate immediately gets the linked Media
objects - as opposed to lazy fetching, which means that the second query will not be done immediately, but only when necessary (when you access the medias
member variable).
Upvotes: 1