[JIRA] (HHH-16493) OneToManyCollectionPart doesn't disassemble properly embeddable IdClass
by Alina Ricciuti (JIRA)
Alina Ricciuti ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=61fb92a... ) *created* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiYzMyYjM0YTFh... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16493?atlOrigin=eyJpIjoiYzMyYj... ) HHH-16493 ( https://hibernate.atlassian.net/browse/HHH-16493?atlOrigin=eyJpIjoiYzMyYj... ) OneToManyCollectionPart doesn't disassemble properly embeddable IdClass ( https://hibernate.atlassian.net/browse/HHH-16493?atlOrigin=eyJpIjoiYzMyYj... )
Issue Type: Bug Affects Versions: 6.2.0, 6.2.1 Assignee: Unassigned Components: hibernate-core Created: 19/Apr/2023 06:08 AM Environment: Hibernate: 6.2.0.Final
Postgresql: 11
Spring Data JPA: 3.0.4
Spring Boot: 3.0.5
JDK: Oracle OpenJDK 17.0.2
OS: Windows 11 Priority: Major Reporter: Alina Ricciuti ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=61fb92a... )
With https://hibernate.atlassian.net/browse/HHH-16281 the disassembling of OneToMany collections doesn’t work properly and renders the identifier object as a collection of objects ( instead of provided identifier type).
Actually the identifier seems to be disassembled twice, first on NonAggregatedIdentifierMappingImpl.getIdentifier for the Entity object and secondly on AbstractEmbeddableMapping.dissamble of the identifier object.
As result, an IllegalArgumentException is thrown when setting the identifier values:
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Long com.example.demo.domain.issue.DoubleId.id] by reflection for persistent property com.example.demo.domain.issue.DoubleId#id ( #id ) : [Ljava.lang.Object;@cc46555
{{ at app//org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:48)}}
{{ at app//org.hibernate.metamodel.mapping.AttributeMapping.getValue(AttributeMapping.java:59)}}
{{ at app//org.hibernate.metamodel.mapping.internal.AbstractEmbeddableMapping.breakDownJdbcValues(AbstractEmbeddableMapping.java:594)}}
{{ at app//org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl.breakDownJdbcValues(NonAggregatedIdentifierMappingImpl.java:299)}}
{{ at app//org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart.breakDownJdbcValues(OneToManyCollectionPart.java:98)}}
{{ at app//org.hibernate.metamodel.mapping.ModelPart.decompose(ModelPart.java:194)}}
{{ at app//org.hibernate.persister.collection.OneToManyPersister.applyDeleteRowRestrictions(OneToManyPersister.java:640)}}
{{ at app//org.hibernate.persister.collection.mutation.DeleteRowsCoordinatorStandard.deleteRows(DeleteRowsCoordinatorStandard.java:97)}}
{{ at app//org.hibernate.persister.collection.OneToManyPersister.deleteRows(OneToManyPersister.java:194)}}
{{ at app//org.hibernate.action.internal.CollectionUpdateAction.execute(CollectionUpdateAction.java:83)}}
...
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Long field com.example.demo.domain.issue.DoubleId.id to [Ljava.lang.Object;
...
{{ at java.base/java.lang.reflect.Field.get(Field.java:425)}}
{{ at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:44)}}
{{ ... 115 more}}
My sample project uses Lombok / Spring Data.
* a Content class with inverse OnToMany reference:
@Entity
@Getter
@Setter
public class Content {
@EmbeddedId
private PkComposite id;
private String name;
@OneToMany(cascade = ALL, orphanRemoval = true)
@JoinColumn(name = "ID_CONTENT", referencedColumnName = "ID")
private List<Detail> detailList = new ArrayList<>();
}
* its composite ID class:
@Getter
@Setter
public class PkComposite implements Serializable {
@Column(name = "ID")
private Long id;
public PkComposite withId(Long id) {
this.id = id;
return this;
}
}
* a Detail class using a composite key defined with @IdClass:
@Entity
@Getter
@Setter
@IdClass(DoubleId.class)
public class Detail {
@Id
private Long id;
@Id
private Long id2;
private String name;
}
* its composite key:
@Getter
@Setter
public class DoubleId implements Serializable {
private Long id;
private Long id2;
}
* the test that fails with the exception detailed above:
@Test
@Transactional
public void issue4_disassamble_idclass() {
// create some data
Session session = entityManager.unwrap(Session.class);
session.createNativeQuery("insert into CONTENT (ID, NAME) values (200, 'NAME for CONTENT 320')").executeUpdate();
session.createNativeQuery("insert into DETAIL (ID, ID2, NAME, ID_CONTENT) values (300, 400, 'NAME for DETAIL 300', 200)").executeUpdate();
// Commit transaction
TestTransaction.flagForCommit();
TestTransaction.end();
TestTransaction.start();
Content content200 = contentRepository.findById(new PkComposite().withId(200L)).orElse(null);
Assertions.assertNotNull(content200);
Assertions.assertNotNull(content200.getDetailList());
Assertions.assertEquals(1, content200.getDetailList().size());
// content200.getDetailList().clear();
content200.getDetailList().remove(content200.getDetailList().get(0));
Detail newDetail = new Detail();
newDetail.setId(301L);
newDetail.setId2(901L);
newDetail.setName("NAME for DETAIL 301");
content200.getDetailList().add(newDetail);
Content content200Upd = contentRepository.save(content200);
Assertions.assertEquals(1, content200Upd.getDetailList().size());
TestTransaction.flagForCommit();
TestTransaction.end();
TestTransaction.start();
}
I have attached the tests logs. If needed, I can share my sample project.
( https://hibernate.atlassian.net/browse/HHH-16493#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16493#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100222- sha1:cb3bdf0 )
1 year, 6 months
[JIRA] (HHH-16469) Hibernate repeatedly issues identical SELECT queries to load an optional one-to-one association
by Andrea Boriero (JIRA)
Andrea Boriero ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *commented* on HHH-16469 ( https://hibernate.atlassian.net/browse/HHH-16469?atlOrigin=eyJpIjoiZTAyND... )
Re: Hibernate repeatedly issues identical SELECT queries to load an optional one-to-one association ( https://hibernate.atlassian.net/browse/HHH-16469?atlOrigin=eyJpIjoiZTAyND... )
Hi Puhong You ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) ,
I have created this test
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@DomainModel(
annotatedClasses = {
TestIt.Agency.class,
TestIt.AgencyDetail.class,
TestIt.User.class,
TestIt.Group.class
}
)
@SessionFactory(useCollectingStatementInspector = true)
@TestForIssue(jiraKey = "HHH_16469")
public class TestIt {
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Agency agency = new Agency( 1, "Test Agency" );
session.persist( agency );
Group group = new Group( 1, "Test Group 1" );
session.persist( group );
for ( int i = 1; i < 9; i++ ) {
User user = new User( i, "User " + i );
group.addUser( user );
agency.addUser( user );
session.persist( user );
}
}
);
}
@Test
public void tesGetAgency(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
Agency agency = session.get( Agency.class, 1 );
List<String> executedQueries = statementInspector.getSqlQueries();
assertThat( executedQueries.size()).isEqualTo( 3 );
assertThat( executedQueries.get( 0 ) ).contains( " from AGENCY_TABLE " );
assertThat( executedQueries.get( 1 ) ).contains( "from USER_TABLE " );
assertThat( executedQueries.get( 2 ) ).contains( "from GROUP_TABLE " );
}
);
}
@Entity(name = "Agency")
@Table(name = "AGENCY_TABLE")
public static class Agency {
private Integer agencyId;
private String agencyName;
private AgencyDetail agencyDetail;
private Set<User> users;
private Set<Group> groups;
public Agency() {
}
public Agency(Integer agencyId, String agencyName) {
this.agencyId = agencyId;
this.agencyName = agencyName;
}
@Id
@Column(name = "AGENCY_ID")
public Integer getAgencyId() {
return agencyId;
}
public void setAgencyId(Integer agencyId) {
this.agencyId = agencyId;
}
@Column(name = "AGENCY_TXT")
public String getAgencyName() {
return agencyName;
}
public void setAgencyName(String agencyName) {
this.agencyName = agencyName;
}
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@PrimaryKeyJoinColumn(name = "AGENCY_ID")
public AgencyDetail getAgencyDetail() {
return agencyDetail;
}
public void setAgencyDetail(AgencyDetail agencyDetail) {
this.agencyDetail = agencyDetail;
}
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.EAGER,
mappedBy = "agency")
@Fetch(FetchMode.SELECT)
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public void addUser(User user){
if(users == null){
users = new HashSet<>();
}
users.add( user );
user.setAgency( this );
}
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.EAGER,
mappedBy = "agency")
@Fetch(FetchMode.SELECT)
public Set<Group> getGroups() {
return groups;
}
public void setGroups(Set<Group> groups) {
this.groups = groups;
}
}
@Entity(name = "AgencyDetail")
@Table(name = "AGENCY_DETAIL_TABLE")
public static class AgencyDetail {
private Integer agencyId = null;
private String agencyDetail = null;
@Id
@Column(name = "AGENCY_ID")
public Integer getAgencyId() {
return agencyId;
}
public void setAgencyId(Integer agencyId) {
this.agencyId = agencyId;
}
@Column(name = "AGENCY_DETAIL")
public String getAgencyDetail() {
return agencyDetail;
}
public void setAgencyDetail(String agencyDetail) {
agencyDetail = agencyDetail;
}
}
@Entity(name = "User")
@Table(name = "USER_TABLE")
public static class User {
private Integer userId;
private String userName;
private Agency agency;
public User() {
}
public User(Integer userId, String userName) {
this.userId = userId;
this.userName = userName;
}
@Id
@Column(name = "USER_ID")
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
@Column(name = "USER_NAME")
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@ManyToOne
@JoinColumn(name = "AGENCY_ID")
public Agency getAgency() {
return agency;
}
public void setAgency(Agency agency) {
this.agency = agency;
}
}
@Entity(name = "Group")
@Table(name = "GROUP_TABLE")
public static class Group {
private Integer groupId;
private String name;
private Agency agency;
private Set<User> users;
public Group() {
}
public Group(Integer groupId, String name) {
this.groupId = groupId;
this.name = name;
}
@Id
@Column(name = "GROUP_ID")
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
@Column(name = "GROUP_NAME", nullable = false, unique = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinTable(
name = "GROUP_USER",
joinColumns = @JoinColumn(name = "GROUP_ID"),
inverseJoinColumns = @JoinColumn(name = "USER_ID"))
@Fetch(FetchMode.SUBSELECT)
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public void addUser(User user) {
if ( users == null ) {
users = new HashSet<>();
}
users.add( user );
}
@ManyToOne
@JoinColumn(name = "AGENCY_ID")
public Agency getAgency() {
return agency;
}
public void setAgency(Agency agency) {
this.agency = agency;
}
}
}
and
session.get( Agency.class, 1 );
executes only 3 queries
List<String> executedQueries = statementInspector.getSqlQueries();
assertThat( executedQueries.size()).isEqualTo( 3 );
that are
Hibernate: select a1_0.AGENCY_ID,a2_0.AGENCY_ID,a2_0.AGENCY_DETAIL,a1_0.AGENCY_TXT from AGENCY_TABLE a1_0 left join AGENCY_DETAIL_TABLE a2_0 on a2_0.AGENCY_ID=a1_0.AGENCY_ID where a1_0.AGENCY_ID=?
[subsystem] TRACE g.hibernate.orm.jdbc.bind JdbcBindingLogging:28 - binding parameter [1] as [INTEGER] - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:39 - extracted value ([2] : [INTEGER]) - [null]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:39 - extracted value ([3] : [VARCHAR]) - [null]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([4] : [VARCHAR]) - [Test Agency]
Hibernate: select u1_0.AGENCY_ID,u1_0.USER_ID,u1_0.USER_NAME from USER_TABLE u1_0 where u1_0.AGENCY_ID=?
[subsystem] TRACE g.hibernate.orm.jdbc.bind JdbcBindingLogging:28 - binding parameter [1] as [INTEGER] - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [2]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 2]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [3]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 3]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [4]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 4]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [5]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 5]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [6]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 6]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [7]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 7]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([1] : [INTEGER]) - [1]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([2] : [INTEGER]) - [8]
[subsystem] TRACE ibernate.orm.jdbc.extract JdbcExtractingLogging:28 - extracted value ([3] : [VARCHAR]) - [User 8]
and
Hibernate: select g1_0.AGENCY_ID,g1_0.GROUP_ID,g1_0.GROUP_NAME from GROUP_TABLE g1_0 where g1_0.AGENCY_ID=?
[subsystem] TRACE g.hibernate.orm.jdbc.bind JdbcBindingLogging:28 - binding parameter [1] as [INTEGER] - [1]
am I miissing something?
( https://hibernate.atlassian.net/browse/HHH-16469#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16469#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100222- sha1:cb3bdf0 )
1 year, 6 months
[JIRA] (HHH-16469) Hibernate repeatedly issues identical SELECT queries to load an optional one-to-one association
by Andrea Boriero (JIRA)
Andrea Boriero ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZTE0OGEyMmYw... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16469?atlOrigin=eyJpIjoiZTE0OG... ) HHH-16469 ( https://hibernate.atlassian.net/browse/HHH-16469?atlOrigin=eyJpIjoiZTE0OG... ) Hibernate repeatedly issues identical SELECT queries to load an optional one-to-one association ( https://hibernate.atlassian.net/browse/HHH-16469?atlOrigin=eyJpIjoiZTE0OG... )
Change By: Andrea Boriero ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
*Object models:
An Agency contains many users. Each user belongs to 0 or more groups. An Agency can optionally have detail information.
*Database tables:
AGENCY:
AGENCY_ID integer not null primary key,
AGENCY_NAME varchar(1024) not null
AGENCY_DETAIL:
AGENCY_ID integer not null primary key,
AGENCY_DETAIL varchar(1024) null
USER:
USER_ID integer not null primary key,
USER_NAME varchar(256) not null,
AGENCY_ID integer not null
GROUP:
GROUP_ID integer not null primary key,
GROUP_NAME varchar(256) not null,
AGENCY_ID integer not null
GROUP_USER:
GROUP_ID integer not null,
USER_ID integer not null,
composite key (GROUP_ID, USER_ID)
*Entity Beans
#Agency.java --
{ { noformat} @Entity }}
{{ @Table(name = "AGENCY", schema = "TEST") }}
{{ public class Agency { }}
{{ private Integer agencyId; }}
{{ private String agencyName; }}
{{ private AgencyDetail agencyDetail; }}
{{ private Set<User> users; }}
{{ private Set<Group> groups; }}
{{ @Id }}
{{ @Column(name = "AGENCY_ID") }}
{{ public Integer getAgencyId() { }}
{{ return agencyId; }}
{{ } }}
{{ public void setAgencyId(Integer agencyId) { }}
{{ this.agencyId = agencyId; }}
{{ } }}
{{ @Column(name = "AGENCY_TXT") }}
{{ public String getAgencyName() { }}
{{ return agencyName; }}
{{ } }}
{{ public void setAgencyName(String agencyName) { }}
{{ this.agencyName = agencyName; }}
{{ } }}
{{ @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) }}
{{ @PrimaryKeyJoinColumn(name = "AGENCY_ID") }}
{{ public AgencyDetail getAgencyDetail() { }}
{{ return agencyDetail; }}
{{ } }}
{{ public void setAgencyDetail(AgencyDetail agencyDetail) { }}
{{ this.agencyDetail = agencyDetail; }}
{{ } }}
{{ @OneToMany( }}
{{ cascade = CascadeType.ALL, }}
{{ orphanRemoval = true, }}
{{ fetch = FetchType.EAGER, }}
{{ mappedBy = "agency") }}
{{ @Fetch(FetchMode.SELECT) }}
{{ public Set<User> getUsers() { }}
{{ return users; }}
{{ } }}
{{ public void setUsers(Set<User> users) { }}
{{ this.users = users; }}
{{ } }}
{{ @OneToMany( }}
{{ cascade = CascadeType.ALL, }}
{{ orphanRemoval = true, }}
{{ fetch = FetchType.EAGER, }}
{{ mappedBy = "agency") }}
{{ @Fetch(FetchMode.SELECT) }}
{{ public Set<Group> getGroups() { }}
{{ return groups; }}
{{ } }}
{{ public void setGroups(Set<Group> groups) { }}
{{ this.groups = groups; }}
{{ } }}
} { { noformat } }}
#AgencyDetail.java --
{ { noformat} @Entity }}
{{ @Table(name = "AGENCY_DETAIL", schema = "TEST") }}
{{ public class AgencyDetail { }}
{{ private Integer agencyId = null; }}
{{ private String agencyDetail = null; }}
{{ @Id }}
{{ @Column(name = "AGENCY_ID") }}
{{ public Integer getAgencyId() { }}
{{ return agencyId; }}
{{ } }}
{{ public void setAgencyId(Integer agencyId) { }}
{{ this.agencyId = agencyId; }}
{{ } }}
{{ @Column(name = "AGENCY_DETAIL") }}
{{ public String getAgencyDetail() { }}
{{ return agencyDetail; }}
{{ } }}
{{ public void set AgencyDetail(String agencyDetail) { }}
{{ agencyDetail = agencyDetail; }}
{{ } }}
} { { noformat } }}
#User.java
{noformat} @Entity
@Table(name = "USER", schema = "TEST")
public class User {
{noformat} private Integer userId;
private String userName;
private Agency agency;
@Id
@Column(name = "USER_ID")
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
userId = userId;
}
@Column(name = "USER_NAME")
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@ManyToOne
@JoinColumn(name = "AGENCY_ID")
public Agency getAgency() {
return agency;
}
public void setAgency(Agency agency) {
this.agency = agency;
}
} {noformat}
}
#Group.java
{ { noformat} @Entity }}
{{ @Table(name = "GROUP", schema = "TEST") }}
{{ public class Group { }}
{{ private Integer groupId; }}
{{ private String name; }}
{{ private Agency agency; }}
{{ private Set<User> users; }}
{{ @Id }}
{{ @Column(name = "GROUP_ID") }}
{{ public Integer getGroupId() { }}
{{ return groupId; }}
{{ } }}
{{ public void setGroupId(Integer groupId) { }}
{{ this.groupId = groupId; }}
{{ } }}
{{ @Column(name = "GROUP_NAME", nullable = false, unique = false) }}
{{ public String getName() { }}
{{ return name; }}
{{ } }}
{{ public void setName(String name) { }}
{{ this.name = name; }}
{{ } }}
{{ @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) }}
{{ @JoinTable( }}
{{ name = "GROUP_USER", }}
{{ schema = "CRISOP", }}
{{ joinColumns = @JoinColumn(name = "GROUP_ID"), }}
{{ inverseJoinColumns = @JoinColumn(name = "USER_ID")) }}
{{ @Fetch(FetchMode.SUBSELECT) }}
{{ public Set<User> getUsers() { }}
{{ return users; }}
{{ } }}
{{ public void setUsers(Set<User> users) { }}
{{ this.users = users; }}
{{ } }}
{{ @ManyToOne }}
{{ @JoinColumn(name = "AGENCY_ID") }}
{{ public Agency getAgency() { }}
{{ return agency; }}
{{ } }}
{{ public void setAgency(Agency agency) { }}
{{ this.agency = agency; }}
{{ } }}
} { { noformat } }}
*Database table contents
INSERT INTO TEST.AGENCY VALUES(1, ‘TEST AGENCY’);
INSERT INTO TEST.USER VALUES (1, 'TEST USER 1);
INSERT INTO TEST.USER VALUES (2, 'TEST USER 2);
INSERT INTO TEST.USER VALUES (3, 'TEST USER 3);
INSERT INTO TEST.USER VALUES (4, 'TEST USER 4);
INSERT INTO TEST.USER VALUES (5, 'TEST USER 5);
INSERT INTO TEST.USER VALUES (6, 'TEST USER 6);
INSERT INTO TEST.USER VALUES (7, 'TEST USER 7);
INSERT INTO TEST.USER VALUES (8, 'TEST USER 8);
INSERT INTO TEST.GROUP VALUES(1, ‘TEST GROUP 1’);
INSERT INTO TEST.GROUP_USER VALUES (1,1);
INSERT INTO TEST.GROUP_USER VALUES (1,2);
INSERT INTO TEST.GROUP_USER VALUES (1,3);
INSERT INTO TEST.GROUP_USER VALUES (1,4);
*Test code
Agency agency = session.get(Agency.class, 1);
* Problems
load the Agency object from DB using the above test code generates 4 identical queries --
~[04/13/23 14:53:43.425] DEBUG : main : SQL - select t1_0.AGENCY_ID,t1_0.AGENCY_DETAIL from TEST.AGENCY_DETAIL t1_0 where t1_0.AGENCY_ID=?~
~[04/13/23 14:53:43.425] TRACE : main : bind - binding parameter [1] as [INTEGER] - [1]~
~[04/13/23 14:53:43.514] DEBUG : main : SQL - select t1_0.AGENCY_ID,t1_0.AGENCY_DETAIL from TEST.AGENCY_DETAIL t1_0 where t1_0.AGENCY_ID=?~
~[04/13/23 14:53:43.515] TRACE : main : bind - binding parameter [1] as [INTEGER] - [1]~
~[04/13/23 14:53:43.601] DEBUG : main : SQL - select t1_0.AGENCY_ID,t1_0.AGENCY_DETAIL from TEST.AGENCY_DETAIL t1_0 where t1_0.AGENCY_ID=?~
~[04/13/23 14:53:43.601] TRACE : main : bind - binding parameter [1] as [INTEGER] - [1]~
~[04/13/23 14:53:43.692] DEBUG : main : SQL - select t1_0.AGENCY_ID,t1_0.AGENCY_DETAIL from TEST.AGENCY_DETAIL t1_0 where t1_0.AGENCY_ID=?~
~[04/13/23 14:53:43.692] TRACE : main : bind - binding parameter [1] as [INTEGER] - [1]~
If I add four more rows into the GROUP_USER table:
INSERT INTO TEST.GROUP_USER VALUES (1,5);
INSERT INTO TEST.GROUP_USER VALUES (1,6);
INSERT INTO TEST.GROUP_USER VALUES (1,7);
INSERT INTO TEST.GROUP_USER VALUES (1,8);
then loading the Agency object from DB using the test code generates 8 identical queries to SELECT from the AGENCY_DETAIL table.
In our project we have an agency with 5000+ users and 100+ groups, and thousands of rows in the GROUP_USER table. It took minutes to load the agency because the same SELECT from the AGENCY_DETAIL table query was issued thousands of times.
If I insert a row into the AGENCY_DETAIL table for agency with id 1 --
INSERT INTO TEST.AGENCY_DETAIL VALUES (1, ‘DETAIL’);
then loading the Agency object using the test code finished quickly, and there is no repeatedly identical SELECT from AGENCY_DETAIL query.
( https://hibernate.atlassian.net/browse/HHH-16469#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16469#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100222- sha1:cb3bdf0 )
1 year, 6 months