Shared column between FK and PK - java

I have two tables, A and B (not defined by me, inherited from old legacy software), with the following structure:
A: PK (idA, ente),
B: PK(idB, ente), FK(idA, ente) --> A
I need to use #IdClass/#Id, rather than #Embeddeble/#EmbeddebleId, because #GeneratedValue on id columns.
I'd like to insert a new couple of rows this way:
A a = new A();
a.setEnte('E1');
a = repA.save(a);
B b = new B();
b.setA(a);
b = repB.save(b);
I use Spring Data JPA's repositories class. My best try, was:
#Getter
#ToString
#Setter
#Entity
#Table(name = "address")
#IdClass(BPK.class)
#NoArgsConstructor
public class B implements Serializable{
#ManyToOne
#JoinColumns({
#JoinColumn(name="idA", referencedColumnName="idA", nullable=false, insertable=false, updatable=false),
#JoinColumn(name="ente", referencedColumnName="ente", nullable=false, insertable=false, updatable=false)
})
private A a;
#Id
#org.hibernate.annotations.GenericGenerator(name = "incrementGenerator", strategy = "org.hibernate.id.IncrementGenerator")
#GeneratedValue(generator="incrementGenerator")
#Column(name = "idB", columnDefinition = "int(10)", nullable = false)
private Integer idB;
#Id
#Column(insertable=false, updatable=false, unique=true, nullable=false, length=6)
private String ente;
}
#Getter
#ToString
#Setter
#Entity
#Table(name = "Users")
#IdClass(APK.class)
#NoArgsConstructor
public class A implements Serializable {
#Id
#org.hibernate.annotations.GenericGenerator(name = "incrementGenerator", strategy = "org.hibernate.id.IncrementGenerator")
#GeneratedValue(generator="incrementGenerator")
#Column(name = "idA", columnDefinition = "int(10)", nullable = false)
private Integer idA;
#Id
#Column(name = "ente", columnDefinition = "char(6)", nullable = false)
public String ente;
}
This solution doesn't work, because idA and ente are not inserted in save(b) instruction. If I change insertable=true in the #ManyToOne lines, there will be error on ente field.
Any suggestions?

Your b.setA() method could also set this automatically
Thanks to Christian Beikov's suggestion, I found an acceptable solution to the problem.
Why it should not infer data directly from the entities is not clear to me.
However, the solution is to force the missing data to be set before persisting using #PrePersist.
The model for B becomes:
#Getter
#ToString
#Setter
#Entity
#Table(name = "address")
#IdClass(BPK.class)
#NoArgsConstructor
public class B implements Serializable{
#ManyToOne
#JoinColumns({
#JoinColumn(name="idA", referencedColumnName="idA", nullable=false, insertable=false, updatable=false),
#JoinColumn(name="ente", referencedColumnName="ente", nullable=false, insertable=false, updatable=false)
})
private A a;
#Id
#org.hibernate.annotations.GenericGenerator(name = "incrementGenerator", strategy = "org.hibernate.id.IncrementGenerator")
#GeneratedValue(generator="incrementGenerator")
#Column(name = "idB", columnDefinition = "int(10)", nullable = false)
private Integer idB;
#Id
#Column(insertable=false, updatable=false, unique=true, nullable=false, length=6)
private String ente;
//New lines (In the real code I used getters, of course) <--
#Column(name = "idA", nullable = false, columnDefinition = "int(10)")
private Integer idA;
#PrePersist
private void init() {
this.ente = a.ente;
this.idA= a.idA;
}
}

Related

How to simulate lombok #data stackoverflow error within unit tests for repository class?

I have a relationship between entities that throws a stack overflow error if the #Data annotation from Lombok is used instead of the individual #Getter and #Setter annotations. This is fixed now, but I would like to write a unit test for it within my repository tests. However, I'm not sure how to achieve that and haven't been able to find samples for it.
Here are my entity classes:
#Entity
#Table(name = "users")
#Builder
//#Getter
//#Setter
#Data
#AllArgsConstructor
#NoArgsConstructor
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private UUID id;
#Column(name = "name")
private String name;
#ManyToMany
#JoinTable(
name = "users_hobbies",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "hobby_and_interest_id", referencedColumnName = "id"))
private Set<HobbyAndInterestEntity> hobbyAndInterestEntities;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
#JoinColumn(name = "hometown_id", referencedColumnName = "id")
private HometownEntity hometownEntity;
#Entity
#Table(name = "hometown")
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
public class HometownEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private UUID id;
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
#OneToMany(mappedBy = "hometownEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = false)
private Set<UserEntity> userEntitySet;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class HobbyAndInterestEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private UUID id;
#Column(name = "title")
private String title;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "hobbyAndInterestEntities")
private Set<UserEntity> userEntities;
And here is my test for a case without the exception, which I was aiming to modify to test for the exception scenario:
#Test
void testGetUser() {
UserEntity userEntity = saveUserEntity();
assertTrue(userRepository.findAll().size() > 0);
userEntity = userRepository.findById(userEntity.getId()).orElse(null);
assertNotNull(userEntity);
UserEntity finalUserEntity = userEntity;
assertAll(
() -> assertEquals("anyName", finalUserEntity.getName()),
() -> assertEquals("anyCountry", finalUserEntity.getHometownEntity().getCountry()),
() -> assertTrue(finalUserEntity.getHobbyAndInterestEntities().size() > 0));
finalUserEntity.getHobbyAndInterestEntities().forEach(h -> assertEquals("anyInterest", h.getTitle()));
}
#NotNull
private UserEntity saveUserEntity() {
HometownEntity hometownEntity = HometownEntity.builder().city("anyCity").country("anyCountry").build();
hometownEntity = hometownRepository.save(hometownEntity);
HobbyAndInterestEntity hobbyAndInterestEntity = HobbyAndInterestEntity.builder()
.title("anyInterest")
.build();
hobbyAndInterestEntity = hobbyAndInterestRepository.save(hobbyAndInterestEntity);
Set<HobbyAndInterestEntity> hobbyAndInterestEntities = new HashSet<>();
hobbyAndInterestEntities.add(hobbyAndInterestEntity);
UserEntity userEntity = UserEntity.builder()
.name("anyName")
.hometownEntity(hometownEntity)
.hobbyAndInterestEntities(hobbyAndInterestEntities)
.build();
return userRepository.save(userEntity);
}
So in summary, I know the application is throwing the stack overflow when I have the #Data annotation and so I would like to write a test that would fail for it and pass again when I modify the entity class to use #Getter and #Setter, but not sure what is needed here and would appreciate some guidance, please.
Thank you very much.
Could you check #Data annotation here. #Data is a shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, #Setter on all non-final fields, and #RequiredArgsConstructor! When you call toString or equals or hashCode method, the relationship entities will query in the database. You can try to review generated source, the relationship entities is used in those methods. I think it can throw a stack overflow error.

Stackoverflowerror occurs even after using jsonProperties. How to solve it?

A.class
#Getter
#Entity
#Table(name = "A")
public class A extends JpaEntity<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "aId", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "aId")
#Setter
private Set<relationship_table_between_a_and_b> bOfa;
}
relationship_table_between_a_and_b.class
#Entity
#Table(name = "relationship_table_between_a_and_b")
#Getter
public static relationship_table_between_a_and_b extends JpaEntity<relationship_table_between_a_and_b.Id> {
#EmbeddedId
#Setter
private Id id;
#Embeddable
#Getter
public static class Id implements Serializable {
#Setter
#Column(name="aId", nullable = false)
private Long aId;
#Setter
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name="bId", nullable = false)
private B b;
}
}
B.class
public static B extends JpaEntity<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "bId", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "relationship_table_between_b_and_a",
joinColumns = #JoinColumn(name = "bId", nullable = false),
inverseJoinColumns = #JoinColumn(name = "aId", nullable = false))
#Setter
#JsonIgnoreProperties("bOfa")
private List<A> as;
}
I tried several ways but didn't solve the stackoverflowerror
Could not solve the infinite reference that occurs when as of class B refers to bToa of a again.
I want to solve this problem... please help

Problems with Hibernate Mapping

I'm having problem with mapping two classes with composite keys.
The first class is Product:
#Entity
#Table(name = "Products")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#SuperBuilder
public class Product {
#EmbeddedId
private ProductKey prodPK;
#Column(name = "name", length = 50, nullable = false)
private String name;
#Column(name = "description", length = 80)
private String description;
#Column(name = "totalStock", columnDefinition = "double(8,2) default 0")
private double totalStock;
#ManyToOne
#JoinColumn(name = "companyId", referencedColumnName = "id", nullable = false)
private Company company;
}
With this #EmbeddedId:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Embeddable
public class ProductKey implements Serializable {
#Column(name = "sku", length = 50)
private String sku;
#Embedded
private LotKey lot;
}
At the same time, this embedded class has as part of its composite key another composite key "LotKey"
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Embeddable
public class LotKey implements Serializable {
#Column(name = "lot")
private String lot;
#ManyToOne
#JoinColumn(name = "company", referencedColumnName = "id")
private Company company;
}
which belongs to the class:
#Entity
#Table(name = "Lots")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#SuperBuilder
public class Lote {
#EmbeddedId
private LotKey lotpk;
#Column(name = "stock")
private double stock;
#Column(name = "expirationDate", columnDefinition = "default current_timestamp()")
private Date expirationDate;
}
But I'm having trouble referencing to them:
#Entity
#Table(name = "quantityProduct")
public class QuantityProduct{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "sku",
referencedColumnName = "sku"),
#JoinColumn(
name = "lot")
})
private Product product;
#Column(name = "quantity", columnDefinition = "double(8,2) default 0")
private double quantity;
}
I am getting the following error
image
Thank you so much !
In QuantityProduct, set also referencedColumnName in
#JoinColumn(
name = "lot")

Repeated column in mapping for entity in #OneToOne unidirectional mapping

Consider the following database structure
I need to implement unidirectional one to one mapping like that (structure is simplified):
#Entity
#Table(name = "entity")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name = "customer_info", nullable = false)
private CustomerInfo customerInfo;
#OneToOne
#JoinColumn(name = "customer_credentials", nullable = false)
private CustomerCredentials customerCredentials;
// getter, setters etc
}
#Entity
#Table(name = "customer_info")
public class CustomerInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc
}
#Entity
#Table(name = "customer_credentials")
public class CustomerCredentials {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc
}
But somehow hibernate unable to differentiate that those joins are from different tables and throws such error:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.example.Customer column: customer_id (should be mapped with insert="false" update="false")
Important notice: I do not want to use #OneToOne(mappedBy = "customer") because I need cascade save functionality
You can use #JoinTable instead of #JoinColumn to solve your problem:
#Entity #Table(name = "entity") public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne(cascade = CascadeType.ALL, targetEntity = CustomerInfo.class)
#JoinTable(name = "customer_info", inverseJoinColumns = {#JoinColumn(name = "customer_id", nullable = false)})
private CustomerInfo customerInfo;
#OneToOne(cascade = CascadeType.ALL, targetEntity = CustomerCredentials.class)
#JoinTable(name = "customer_credentials", inverseJoinColumns = {#JoinColumn(name = "customer_id", nullable = false)})
private CustomerCredentials customerCredentials;
// getter, setters etc }
#Entity #Table(name = "customer_info") public class CustomerInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc }
#Entity #Table(name = "customer_credentials") public class CustomerCredentials {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc }
You could change the cascade strategy to any strategy you need. I just left CascadeType.ALL there as an example.

JPA unidirectional one to many with join table - entity mapping not working

I have tried to create some JPA Entities for a DB designed with the following tables: PRINCIPALS and CREDENTIALS which have the following relations with other tables:
#Entity
#Table(name = "CREDENTIALS")
public class Credentials {
#Id
#Column(name = "CREDENTIAL_ID")
private Integer credentialID;
#Id
#Column(name = "CREDENTIAL_TYPE_ID")
private String credentialTypeID;
#OneToOne
#JoinColumn(name = "CREDENTIAL_TYPE_ID", insertable = false, updatable = false)
private CredentialTypes credentialTypes;
}
CREDENTIALS has a oneToOne relation with CREDENTIAL_TYPES
#Entity
#Table(name = "CREDENTIAL_TYPES")
public class CredentialTypes {
#Id
#Column(name = "CREDENTIAL_TYPE_ID")
private String credentialTypeID;
#Column(name = "DESCRIPTION")
private String description;
}
#Entity
#Table(name = "PRINCIPALS")
public class Principals implements Serializable {
#Id
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Column(name = "PRINCIPAL_ID", nullable = false)
private String principalID;
#OneToOne
#JoinColumn(name = "PRINCIPAL_TYPE_ID", insertable = false, updatable = false)
private PrincipalTypes principalTypes;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "PRINCIPAL_CREDENTIAL",
joinColumns = #JoinColumn(name = "CREDENTIAL_ID"),
inverseJoinColumns = #JoinColumn(name = "PRINCIPAL_ID"))
private List<Credentials> credentials;
PRINCIPALS has a oneToOne relation with PRINCIPAL_TYPES
#Entity
#Table(name = "PRINCIPAL_TYPES")
public class PrincipalTypes implements Serializable {
#Id
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Column(name = "DESCRIPTION")
private String description;
And finally PRINCIPALS has a oneToMany relation with CREDENTIALS and uses a join table PRINCIPLE_CREDENTIAL
#Entity
#Table(name = "PRINCIPAL_CREDENTIAL")
public class PrincipalCredential implements Serializable {
#Id
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Id
#Column(name = "PRINCIPAL_ID", nullable = false)
private String principalID;
#Id
#Column(name = "CREDENTIAL_ID")
private Integer credentialID;
#Id
#Column(name = "CREDENTIAL_TYPE_ID")
private String credentialTypeID;
At startup (using SpringBoot) I receive an error for the oneToMany relation between Principals and Credentials and just don't have any idea how to fix it... Tried various other methods (The DB design cannot be changed).
Caused by: org.hibernate.AnnotationException: A Foreign key refering entities.Principals from entities.Credentials has the wrong number of column. should be 2
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:502)
at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1467)
at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1233)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:794)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:729)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:70)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1697)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:85
I find the exception wierd because there is no refering of Principlas from Credentials....
PRINCIPLE_TYPE_ID and CREDENTIAL_TYPE_ID are missing in the joinColumns/inverseJoinColumns. I think you must use the #JoinColumns Annotation

Categories

Resources