0

I have an application with a history table mapped to a view on a ManyToOne relation as follows

@Entity
@Table(name = "HISTORY_TABLE", schema = "MY_SCHEMA")
public class HAction implements Serializable {
...

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({    
    @JoinColumn(name = "ID_WHO", referencedColumnName = "ID_USER"),
    @JoinColumn(name = "CT_WHO", referencedColumnName = "CT_SEQUENCE")
    })
private VwUsers whoDidTheAction;

...

//Getters & Setters

}

When running the application, It encountered the famous "More than one row with the given identifier was found".

Therefore, I went into debug to figure out what was the user who was causing this issue. Indeed, it has a duplicate on the database View object. However, during debug, I stumbled upon the following "magic"

Not sure if it has anything to do with my IDE but when using the code inspector on intelliJ (Display on Eclipse) and issuing the following

hAction.getWhoDidTheAction().getId();

It gave me the error, but when doing it for a second time, on the same display "session", no error happened, the result was there as expected.

Anyone knows the source of this behaviour? I couldn't find anything related to this.

full HAction class:

@Entity
@Table(name = "HISTORY_TABLE", schema = "MY_SCHEMA")
public class HAction implements Serializable {

 /**  */
 private static final long serialVersionUID = -294276241063426049 L;

 @Id
 @Column(name = "ID_HISTORY_TABLE")
 @SequenceGenerator(name = "SQ_HISTORY_TABLE", sequenceName = "MY_SCHEMA.SQ_HISTORY_TABLE", allocationSize = 1)
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_HISTORY_TABLE")
 private Long idHAction;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_ACTION", referencedColumnName = "ID_ACTION")
 private SAction action;

 @Transient
 private Long idActionTransient;

 @Enumerated(EnumType.STRING)
 @Column(name = "CS_OPERATION")
 private OperationEnum csOperation;

 @Temporal(TemporalType.TIMESTAMP)
 @Column(name = "DT_OPEARATION")
 private Date dhOPeration;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_USER", referencedColumnName = "ID_USER")
 private VwUser user;

 @Column(name = "CD_PROTOCOL")
 private String cdProtocol;

 @Temporal(TemporalType.DATE)
 @Column(name = "DT_PROTOCOL")
 private Date dtProtocol;

 @Column(name = "DS_ACTION")
 private String dsAction;

 @Temporal(TemporalType.DATE)
 @Column(name = "DT_PREDICTION")
 private Date dtPrediction;

 @Enumerated(EnumType.STRING)
 @Column(name = "CS_ORIGIN")
 private OriginEnum csorigin;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_REQUIRED_BY_SECTOR", referencedColumnName = "ID_SECTOR")
 private VwSector vwSector;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_REQUIRED_BY_HQ", referencedColumnName = "ID_HQ")
 private VwHq vwHq;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_REQUIRED_BY_LOCAL", referencedColumnName = "ID_LOCAL")
 private VwLocal vwLocal;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_REQUIRED_BY_ASSOC", referencedColumnName = "ID_ASSOC")
 private VwAssoc vwAssoc;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_REQUIRED_BY_INTER", referencedColumnName = "ID_INTER")
 private VwInter vwInter;

 @Column(name = "DAY_VL")
 private Double dayValue;

 @Column(name = "MAT_VAL")
 private Double matValue;

 @Column(name = "OP_VALUE")
 private Double opValue;

 @Column(name = "TERC_VALUE")
 private Double tercValue;

 @Column(name = "TERC_SERV_VALUE")
 private Double tercServValue;

 @Column(name = "PUB_VALUE")
 private Double pubValue;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_RESPONSIBLE", referencedColumnName = "ID_UNIT")
 private Units unit;

 //This is the first fragment
 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumns({
  @JoinColumn(name = "ID_BOSS", referencedColumnName = "ID_USER"),
  @JoinColumn(name = "CT_BOSS", referencedColumnName = "CT_SEQUENCE")
 })
 private VwUsers whoDidTheActionBoss;
 //
 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumns({
  @JoinColumn(name = "ID_SUB_BOSS", referencedColumnName = "ID_USER"),
  @JoinColumn(name = "CT_SUB_BOSS", referencedColumnName = "CT_SEQUENCE")
 })
 private VwUsers whoDidTheActionSubBoss;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_PHONE", referencedColumnName = "ID_PHONE")
 private VwPhone vwPhone;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_EMAIL", referencedColumnName = "ID_EMAIL")
 private VwEmail vwEmail;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_EMAIL_SUB_BOSS", referencedColumnName = "ID_EMAIL")
 private VwEmail vwEmailSubBoss;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_PHONE_SUB_BOSS", referencedColumnName = "ID_PHONE")
 private vwPhone vwPhoneSubBoss;

 @Column(name = "PROTOCOL")
 private String protocol;

 @Enumerated(EnumType.STRING)
 @Column(name = "CONN")
 private VwConn conn;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "ID_TYPE", referencedColumnName = "ID_TYPE")
 private SType type;

 @OneToMany(fetch = FetchType.LAZY, mappedBy = "id.HAction")
 private List < SFiles > files;

 @OneToMany(fetch = FetchType.LAZY, mappedBy = "id.HAction")
 private List < SThemes > themes;

 @Column(name = "ST_ARCHIVED")
 private Character archived;

 @Temporal(TemporalType.DATE)
 @Column(name = "DT_VIG")
 private Date dtVig;

 /**
  * Create Object {@link HAction}.
  * 
  */
 public HAction() {}

 //Getters & Setters
}
Joao Pedro
  • 11
  • 2
  • Can we see the whole class please? – Robert Bain May 07 '19 at 21:28
  • The view or the table? both of them are quite big ( > 800 lines) and i'll have to make a lot of redacting. Nothing much other than mapping on both though – Joao Pedro May 07 '19 at 21:35
  • Just the `HAction` class in its entirety to give us the whole picture. – Robert Bain May 07 '19 at 21:37
  • Edited the question with the Class – Joao Pedro May 07 '19 at 21:52
  • Maybe Hibernate loads both views on the first time and throws an error and on the second time it just "skips" the duplicate somehow? I'm trying to understand why throw the Exception on the first try but not afterwards. Maybe add implementation of HashCode? – Joao Pedro May 07 '19 at 22:02

1 Answers1

0

After some more in-depth research with a few coworkers I've been pointed that this might be a proxy implementation behaviour, and indeed it was.

During the "getId()" execution the proxy class checks if the many-to-one relation is satisfied, if not, it fills the result with the data of the first found result THEN throws the exception. This is specially usefull for debugging since it gives you at least one of the duplicate objects. It is the intended behaviour.

Since I already have an object in memory, there is no need to "re-search" the database for the records, therefore the "getId()" method returns the id of the first element and does not throw an exception since it did not query the database again.

Some explanation regarding proxy behaviour can be found here

Some information on the Lazy Initializer can be found here

Thank you to everyone who took their time to help me.

Joao Pedro
  • 11
  • 2