Spring Data JPA XRef With PK Object Mapping - java

I'm having a hard time getting Spring Data to persist the values into an XREF Table. I'm attempting to talk to an existing DB so i cannot change the schema which would have made this easier.
Classes
#Entity
#Table(name="user_type")
class UserType {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="user_role")
class UserRole {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="company")
class Company{
#Id
#Column(name="id")
private UUID id;
#Column(name="name")
private String name;
}
#Entity
#Table(name="user")
class User {
#Id
#Column(name="id")
private UUID id;
#Column(name="email")
private String email;
#ManyToOne
#JoinTable
private UserType userType;
//not sure what to do here or which JoinTable/ManyToOne/Etc
private UserCompanyAccess userCompanyAccess;
}
#Entity
#Table(name="user_company_access")
class UserCompanyAccess {
#EmbeddedId
private UserCompanyAccessId userCompanyAccessId;
// not sure of relationships here either
private User user;
private Company company;
private UserRole userRole;
#Embeddable
static class UserAccessCompanyId implements Serializable {
#Column(name="id")
private UUID id;
}
}
I have tried many different combinations of #JoinColumns specifying the user(id) and userAccessCompany(user_id) as well with company. The code compiles but at runtime hibernate either throws an error saying company_id not provided or other random exceptions regarding trying to compare uuid to character varying etc which was really weird. Any help would be appreciated. I have done many #OneToMany/#ManyToOne/#ManyToMany but somehow never through spring data with an XREF table where the PK is not a composite of the 2 joining tables. That's what is throwing me off. Otherwise the #EmbeddedId would be the combination of the two.

The dark brown section of the spreadsheet is confusing, because it suggests there's a CompanyUser table that you haven't mentioned elsewhere. Is that an issue?
Regardless, it's a shame you can't adjust the schema, because this design has a lot of flaws. However, it should still be possible to use it. I'm a little stale on Java and Spring/Boot, and I don't have a useful way to test them easily, but you may have better luck with these:
#Entity
#Table(name="user")
class User {
#Id
#Column(name="id")
private UUID id;
#Column(name="email")
private String email;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_type", table="user_type", referencedColumnName = "name")
private UserType userType;
}
#Entity
#Table(name="user_type")
class UserType {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="user_role")
class UserRole {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="company")
class Company{
#Id
#Column(name="id")
private UUID id;
#Column(name="name")
private String name;
}
#Entity
#Table(name="user_company_access", uniqueConstraints = {
#UniqueConstraint(columnNames = { "user_id", "company_id" })
})
class UserCompanyAccess {
#Id
#Column(name="id")
private UUID id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role", table="user_role", referencedColumnName = "name")
private UserRole userRole;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", table="user", referencedColumnName = "id")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id", table="company", referencedColumnName = "id")
private Company company;
}
Explanation: The user_company_access table is where everything ultimately gets tied together. It's designed such that any particular combination of user and company to be unique, and #UniqueConstraint enables that in the code; you don't really need an embeddable object for that.

Related

How to map master detail on hibernate?

I can not change the db schema and this is what I got so far:
public class User{
#Id
private String userId;
#OneToMany
#JoinTable(
name = "user_invoice",
joinColumns = #JoinColumn(name="user_id"),
inverseJoinColumns = #JoinColumn(name = "invoice_id")
)
private List<InvoiceItem> invoiceItems;
}
public class InvoiceItem{
#Id
private String invoiceId;
private String invoiceItemId;
}
This configuration does not allow invoice_id to be duplicated on invoice_item table(it should since I can have multiple items on a given invoice)
If I make invoice_item_id composite pk I would need to add an extra column on user_invoice table which I can not.
How can I map this?
You could split up the many-to-many association into two one-to-many associations and an entity for the join table. You can map it like this:
public class User{
#Id
private String userId;
#OneToMany(mappedBy = "user")
private List<Invoice> invoices;
}
#Table(name = "user_invoice")
public class Invoice{
#Id
#ManyToOne(fetch = LAZY)
#JoinColumn(name="user_id")
private User user;
#Id
private String invoiceId;
#OneToMany(mappedBy = "invoice")
private List<InvoiceItem> invoiceItems;
}
public class InvoiceItem{
#Id
#ManyToOne(fetch = LAZY)
private Invoice invoice;
#Id
private String invoiceItemId;
}

Attribute many ids to single from other tables JPA

I want to create entity USERS_GROUP to link some user_ids to group_id.
I guess I overthink this case and now I can't find a solution.
#Entity
#Table(name = "users")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String userId;
private String name;
#OneToMany(mappedBy = "user")
private List<UserGroups> userGroups;
#Table(name = "groups")
#Data
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private String groupId;
private String category;
private String name;
private String description;
#OneToMany(mappedBy = "group")
private List<UserGroups> userGroups;
#Entity
#Data
#Table(name = "user_groups")
public class UserGroups {
#EmbeddedId
UserGroupsCompositeKey id;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
private Users user;
#ManyToOne
#MapsId("featureId")
#JoinColumn(name = "group_id")
private Group group;
#Embeddable
public class UserGroupsCompositeKey implements Serializable {
#Column(name = "user_id")
String userId;
#Column(name = "group_id")
String groupId;
}
I want to sent POST requests like "/group/{group_id}/users"
to send in request body some lists of user_ids to connect them.
I think I overconfigured this solution with a composite key.
Is there some easier and more readable solution for that case ?
EDIT: This solution is not working as I want it to do.
I create jpaRepository class with CompositeKey (Not sure if it's correct way )
#Repository
public interface ProductFeaturesRepository extends JpaRepository<UserGroups, UserGroupsCompositeKey> {
}
And I want to add some Controller method to group class to add some users to group with request body like:
Endpoint:/group/{group_id}/users
Body:
[
{
"userId": "USER-NR423423534634"
},
{
"userId": "USER-NR2355321"
}
]
It's not working properly nothing is added to database after that request

Java JPA how relate an entity instance with all instances of another entity?

I work with an embedded H2 database in which I use the #OneToMany relationship to relate an entity instance (product) to multiple instances of the other entities (suppliers); it's useful when I have specific suppliers for a particular product.
However now, I want to associate all the suppliers with every single product; I don't want to generate in my supplier table different supplier records for each product, instead I want to have only 5 records (5 suppliers) in my supplier table which are associated to every single product, it few words I want to achieve something like "one to all", is it possible to do it using JPA annotations?
Product entity
#Entity
public class Product {
#Id
private String productCode;
#OneToMany
#JoinColumn(name = "supplier_id", referencedColumnName = "productCode")
private List<Supplier> suppliers;
}
Supplier entity
#Entity
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
}
Unidirectional #OneToMany association:
#Entity
public class Product {
#Id
// #Column(name = "id") maybe
// #GeneratedValue maybe
private String productCode;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) // according to your need
private List<Supplier> suppliers;
...
}
And,
#Entity
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
...
}
#ManyToOne association:
#Entity
public class Product {
#Id
// #Column(name = "id") maybe
// #GeneratedValue maybe
private String productCode;
...
}
And,
#Entity
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "product_id", foreignKey = #ForeignKey(name = "PRODUCT_ID_FK"))
private Product product;
private String name;
...
}

#OneToOne relationship with additional constraint

Suppose, we have two entities, first one:
#Entity
#Table(name = "entitya")
public class EntityA {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private Long name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<EntityB> childEntities;
}
and the second:
#Entity
#Table(name = "entityb")
public class EntityB {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "master")
private Boolean master;
#ManyToOne
#JoinColumn(name = "parent")
private EntityA parent;
}
So far, so good. However underlying database tables and constrains enforce that for any entityA there can be only one EntityB with boolean field master set to true. I can extract it by adding following method to entityA:
public entityB getMasterChild() {
for(entityB ent : childEntities) {
if(ent.isMaster()) {
return ent;
}
}
}
The question is, can I create #OneToOne relationship in EntityA that can express that rule, so that entityA can have additional masterChild member of type entityB?
If I understood you correctly you want to create/define a relationship between two entities based on a value of some entity's property. The think is that relationship between entities is defined on entities count (how many entities can has the other entity) and not on some entity's property value.
However
If you really want to use #OneToOne mapping for masterChild I would recommend creating a separate table/entity for it. Once this is done, you can include this new MasterChild entity into EntityA and annotate it with #OneToOne.
Here is new MasterChild entity
#Entity
public class MasterChild extends EntityB{
#Id
#Column(name = "id")
private Long id;
}
Note that I have removed 'master' from EntityB as it is no longer needed
#Entity
#Table(name = "entityb")
public class EntityB {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "parent")
private EntityA parent;
}
And here is modified EntityA
#Entity
#Table(name = "entitya")
public class EntityA {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private Long name;
#OneToOne
private MasterChild master;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<EntityB> childEntities;
}

Hibernate creating relation on #JoinTable

I have two tables which have Many-to-Many relations which have a JoinTable USER_SERVICES as below.
#Entity
public class User implements Serializable {
#NotNull
#Column(unique=true)
private String username;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "USER_SERVICES",
joinColumns = {#JoinColumn(name = "id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "", referencedColumnName = "name")})
private Set<Services> services;
// Getters and Setters
}
#Entity
public class Services implements Serializable {
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long serviceId;
#NotNull
#Column(unique=true)
private String name;
//Getters and Setters
}
The above code creates a table USER_SERVICES, but I also want to have a Many-to-Many relation on the table USER_SERVICES with another table RATINGS which would result in another table USER_SERVICES_RATINGS. how can I define this relation with Hibernate/JPA annotations?
Bi-Directional Many to Many using user managed join table object (Relatively common)
Commonly used when you want to store extra information on the join object such as the date the relationship was created.
public class Foo{
private UUID fooId;
#OneToMany(mappedBy = "bar", cascade=CascadeType.ALL)
private List<FooBar> bars;
}
public class Bar{
private UUID barId;
#OneToMany(mappedBy = "foo", cascade=CascadeType.ALL)
private List<FooBar> foos;
}
#Entity
#Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;
#ManyToOne
#JoinColumn(name = "fooId")
private Foo foo;
#ManyToOne
#JoinColumn(name = "barId")
private Bar bar;
//You can store other objects/fields on this table here.
}
You need to create an explicit UserServices entity and setup the relationship to the Ratings entity per your needs.
Remember that in hibernate you model relationships between entities (i.e. your java objects), not db tables.

Categories

Resources