i am working on Spring Data JPA with Hibernate. I want to create a mapping table using hibernate automatically and want to retrieve the values based on the id's of each individual tables.
CustomerEntity.java:
#Getter
#Setter
#Table(name = "customers")
#Entity
public class CustomerEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "customerId", updatable = false, nullable = false)
private UUID customerId;
#ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "customerEntities")
private List<ItemEntity> itemEntities;
//More customer fields
}
ItemEntity.java
#Getter
#Setter
#Table(name = "items")
#Entity
public class ItemEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "itemId", updatable = false, nullable = false)
private UUID itemId;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "customerId", nullable = false)
private List<CustomerEntity> customerEntities;
//More item fields
}
Now i want to create a mapping table with manyToMany for these two based on the id's
something like below:
MappingTable:
id // Primary Key
customerId // primary key from customer table
itemId // primary key from item table
and i want to use customerId or itemId (depends on which i have available) to find the records within the mapping table.
Can any one please provide any suggestions would be helpful here. TIA.
Related
I'm currently mapping a complex database schema wqith HIbernate and I have hit a wall with an entity which has a composite key with another composite key.
I have this table for roles with a composite key (site_id, id)
CREATE TABLE IF NOT EXISTS core.roles
(
id uuid NOT NULL DEFAULT gen_random_uuid(),
name character varying(100) NOT NULL,
is_system_role boolean NOT NULL DEFAULT FALSE,
site_id uuid NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
created_by uuid NOT NULL,
updated_by uuid NOT NULL,
CONSTRAINT roles_pkey PRIMARY KEY (site_id, id),
CONSTRAINT roles_name_key UNIQUE (site_id, name),
CONSTRAINT roles_site_id_fkey FOREIGN KEY (site_id)
REFERENCES core.sites (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE,
CONSTRAINT roles_created_by_fkey FOREIGN KEY (created_by)
REFERENCES core.users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE RESTRICT,
CONSTRAINT roles_updated_by_fkey FOREIGN KEY (updated_by)
REFERENCES core.users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE RESTRICT
);
And I have this table with a composite key which also uses the previous one.
CREATE TABLE IF NOT EXISTS core.user_site_roles
(
user_id uuid NOT NULL,
site_id uuid NOT NULL,
role_id uuid NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
created_by uuid NOT NULL,
CONSTRAINT user_site_roles_pkey PRIMARY KEY (site_id, user_id, role_id),
CONSTRAINT user_site_roles_site_id_fkey FOREIGN KEY (site_id)
REFERENCES core.sites (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE,
CONSTRAINT user_site_roles_role_id_fkey FOREIGN KEY (site_id, role_id)
REFERENCES core.roles (site_id, id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE,
CONSTRAINT user_site_roles_user_id_fkey FOREIGN KEY (user_id)
REFERENCES core.users (id) MATCH SIMPLE
ON UPDATE NO ACTION
CONSTRAINT user_site_roles_created_by_fkey FOREIGN KEY (created_by)
REFERENCES core.users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE RESTRICT
);
My current mapping for the roles one which is working is:
#Embeddable
public class CommonId implements Serializable {
#Type(type = "pg-id-uuid")
#Column(columnDefinition = "uuid", updatable = false)
private UUID id;
#Type(type = "pg-id-uuid")
#Column(name = "site_id", columnDefinition = "uuid", updatable = false)
private UUID siteId;
}
#Entity
#Table(name = "roles", schema = "core")
#Data
#TypeDefs({
#TypeDef(name = "pg-id-uuid", typeClass = PostgresIdUUIDType.class)
})
public class Role extends AuditAtBy implements Serializable {
#EmbeddedId
private CommonId roleId;
#ManyToOne
#MapsId("siteId")
#OnDelete(action = OnDeleteAction.CASCADE)
private Site site;
#Column(nullable = false, unique = true, length = 100)
private String name;
#Column(name = "is_system_role", nullable = false)
private boolean isSystemRole;
}
I was trying something similar with the composite Key for the UserSiteRole but Hibernate tells me that it needs to columns to map the roleId when in the table I have just the id but the PK is form by the two values as you can see in the script, not sure how to map it to be honest.
#Embeddable
public class UserSiteRoleId implements Serializable {
#Type(type = "pg-id-uuid")
#Column(columnDefinition = "uuid", updatable = false)
private UUID userId;
#Type(type = "pg-id-uuid")
#Column(name = "site_id", columnDefinition = "uuid", updatable = false)
private UUID siteId;
#Type(type = "pg-id-uuid")
#Column(name = "role_id", columnDefinition = "uuid", updatable = false)
private UUID roleId;
}
#Entity
#Table(name = "user_site_roles", schema = "core")
#Data
#TypeDefs({
#TypeDef(name = "pg-id-uuid", typeClass = PostgresIdUUIDType.class)
})
public class UserSiteRole extends AuditCreated implements Serializable {
#EmbeddedId
private UserSiteRoleId userSiteRoleId;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id", columnDefinition = "uuid", nullable = false)
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
#ManyToOne
#MapsId("siteId")
#JoinColumn(name = "site_id", columnDefinition = "uuid", nullable = false)
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private Site site;
#ManyToOne
#MapsId("roleId")
#JoinColumn(name = "role_id", columnDefinition = "uuid", nullable = false)
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private Role role;
}
I would appreciate any ideas about how to map it, I had never had to map such a complex relationship so not sure how to proceed in this case.
Does this answer your question? jpa hibernate composite foreign key mapping
Actually that was useful as it made it clear that we could change the mapping from embeddedId to IdClass and make it work.
This is our new IdClass, pretty simple:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserSiteRoleId implements Serializable {
private User user;
private Site site;
private Role role;
}
And the entity itself working just inf e is as follows.
#Entity
#Table(name = "user_site_roles", schema = "core")
#Data
#TypeDefs({
#TypeDef(name = "pg-id-uuid", typeClass = PostgresIdUUIDType.class)
})
#IdClass(UserSiteRoleId.class)
public class UserSiteRole extends AuditCreated implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "user_id", columnDefinition = "uuid", nullable = false)
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
#Id
#ManyToOne
#JoinColumn(name = "site_id", columnDefinition = "uuid", nullable = false)
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private Site site;
#Id
#ManyToOne
#JoinColumns({
#JoinColumn(name = "role_id", referencedColumnName="id", columnDefinition = "uuid", insertable = false, updatable = false),
#JoinColumn(name = "site_id", referencedColumnName="site_id", columnDefinition = "uuid", insertable = false, updatable = false)
})
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private Role role;
}
Have 2 Entities: Orders and Products. 1 Order can have Many Products and Many Products can belong to 1 Order (Each Product only belongs to 1 Order).
With unidirectional association at Order Entity, I am able to retrieve product details when performing orderRepo.findAll(); In similar fashion, need order details when performing productRepo.findAll();
Tried code:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "order_details")
public class OrderData {
#Id
#Column(name = "order_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
#NotNull
#Column(name = "customer_name", nullable = false)
private String customerName;
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "product_details")
public class ProductData {
#Id
#Column(name = "product_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
#NotNull
#Column(name = "product_name", nullable = false)
private String productName;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", nullable = false)
private OrderData orderData;
}
While inserting at products; we are getting error: "insert or update on table violates foreign key constraint jpa"
While performing productRep.findAll(): infinite loop for hibernate select queries
Tried #JsonIgnore. This not returning child or parent elements.
Tried #JsonManagedReference vs #JsonBackReference - still no luck.
Please guide me on this
The mappedBy attribute points to the wrong field:
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
This must be the back reference:
#OneToMany(mappedBy = "orderData", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
I need encrypted and decrypted some column such.
#Entity
#Table(name = "Person")
#Data
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#Column(name = "NAME", columnDefinition = "varchar")
#ColumnTransformer(read = "pgp_sym_decrypt(decode(NAME, 'hex'), 'key')", write = "encode(pgp_sym_encrypt(?, 'key'), 'hex')")
private String name;
#ManyToOne
#JoinColumn(name = "PARENT_ID")
private Person parent;
}
When I use PersonRepository.findOneByParentId(10L); and see log sql is
select
person0_."id" as id1_99_,
pgp_sym_decrypt(decode(NAME, 'hex'), 'key') as name2_99_
from
"public"."person" person0_
left outer join
"public"."person" person1_
on person0_."parent_id"=person1_."id"
where
person1_."id"=?
This error message
ERROR: column reference "name" is ambiguous
Why #ColumnTransformer don't add table alias of column "name", What should I do ?
I had this issue and adding the forColumn property worked for me.
#ColumnTransformer(
forColumn = "lastName",
I have simple entity with field countUsing which specify count used in other table. It is subquery with annotation Formula.
And i would like in one case ignore execute query in formula but others subquery must invoke.
My entity:
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(generator = "roles_id_seq", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "roles_id_seq", sequenceName = "roles_id_seq", allocationSize = 1)
#Column(name = "id")
private Long id;
#Column(name = "\"NAME\"")
private String name;
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime insertDate;
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime updateDate;
#Formula("(SELECT COUNT(*) FROM user_roles us WHERE us.id_role = id)")
private Integer countUsing;
}
How can i achive the target, i think about flag in any annotation for field countUsing.
Annotation Formula has got only value property.
I understand that you want use FetchType.LAZY on this attribute.
Therefore, you can`t use LAZY directly in a #Formula attribute in your class.
The solution for this is create an wrapper Class, with a oneToOne relationship to your class that`s contain your formula attribute, like this:
#Entity
#Table(name = "roles")
public class Role{
... /*other attributes*/
#OneToOne(fetch = FetchType.LAZY, mappedBy = "role")
private RoleCounting roleCouting;
}
#Entity
#Table(name = "roles")
public class RoleCounting{
#Id
#Column(name="id", insertable = false, updatable = false)
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "id", insertable = false, updatable = false)
private Role role;
#Formula("(SELECT COUNT(*) FROM user_roles us WHERE us.id_role = id)")
private Integer countUsing;
}
That way, the subquery will only be executed when you invoke:
role.getRoleCounting().getCountUsing();
I'm new in hibernate. So, I don't know how to do this:
I have 3 tables:
Table Person:
#Entity
#Table(name = "ASD_PERSON")
public class AsdPerson implements Serializable {
#Id
#SequenceGenerator(name="seq_name", sequenceName="gen_id_value", allocationSize = 1)
#GeneratedValue(generator="seq_name")
#Column(name="F_PERSON_ID", nullable = false)
private Long fPersonId;
#OneToMany(mappedBy = "AsdPerson",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<AsdPersonEvent> asdPersonEventList;
... setters and getters ...
}
Table Event:
#Entity
#Table(name = "ASD_EVENT")
public class AsdEvent implements Serializable {
#Id
#SequenceGenerator(name="seq_name", sequenceName="gen_id_value", allocationSize = 1)
#GeneratedValue(generator="seq_name")
#Column(name="F_EVENT_ID", nullable = false)
private Long fEventId;
#OneToMany(mappedBy = "AsdEvent",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<AsdPersonEvent> asdPersonEventList;
... setters and getters ...
}
Table Person-Event:
#Entity
#Table(name = "ASD_PERSON_EVENT")
#IdClass(AsdPersonEventPK.class)
public class AsdPersonEvent implements Serializable {
#Id
#GenericGenerator(name = "generator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "asdPerson"))
#GeneratedValue(generator = "generator")
#Column(name="F_PERSON_ID", nullable = false, insertable = false,
updatable = false)
private Long fPersonId;
#Id
#GenericGenerator(name = "generator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "asdEvent"))
#GeneratedValue(generator = "generator")
#Column(name="F_EVENT_ID", nullable = false, insertable = false,
updatable = false)
private Long fEventId;
#ManyToOne
#JoinColumn(name = "F_PERSON_ID", insertable = false,
updatable = false)
private AsdPerson asdPerson;
#ManyToOne
#JoinColumn(name = "F_EVENT_ID", insertable = false,
updatable = false)
private AsdEvent asdEvent;
... setters and getters ...
}
Everything works perfectly (adding new records, creating new objects) except the case, when I try to delete associated records from Event table or Person table:
...
AsdEvent ev = getService().get(115); // get record from Event table by id = 115 (for example)
ev.getAsdPersonEventList().remove(1); // delete some existing records
getService().merge(ev);
...
After that I get error:
deleted object would be re-saved by
cascade (remove deleted object from
associations):
[database.AsdPersonEvent#database.AsdPersonEventPK#12908fc]
How to configure Hibernate with annotations or some other way to get rid of this error?
If you have a complex graph of persistent entities, I think you need to give up using orphanRemoval and remove your entities manually using em.remove().
orphanRemoval is designed for simple parent-child relationships, where child doesn't make sense without parent. If in your case child may have ohter relationships, perhaps it's not a good case for orphanRemoval.
Try once again by removing orphanRemoval = true