I have the following table:
#Entity(name = 'STUDENT')
class Student {
#Id
#Column(name = 'STUDENT_ID')
String studentId
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "id.student")
Set<Disabilities> disabilities = []
}
#Entity(name = 'STUDENT_DISABILITY')
class Disability {
#EmbeddedId
DisabilityId id
#Nullable
#Column(name = 'MOD_DT')
LocalDateTime modifiedDate
}
#Embeddable
class DisabilityId implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = 'STUDENT_ID')
Student student
#Column(name = 'DISABILITY_CD')
String disabilityCode
}
This all works fine but I am trying to join a new table 'Disability_info' table to 'Disability' table. This is the Disability_info table:
#Entity
#Table(name = 'DISABILITY_INFO')
class DisabilityInfo {
#Id
#Column(name = 'DISABILITY_CD')
String id
#Column(name = 'DISABILITY_NAME')
String disabilityName
#Column(name = 'DISABILITY_DESC')
String disabilityDesc
}
The problem that I am having is that the DisabilityInfo's primary key is part of the composite key of the Disability class. All I want is the below with sql:
SELECT * FROM DISABILITY a INNER JOIN DISABILITY_INFO b on a.DISABILITY_CD = b.DISABILITY_CD
Can anybody please shed some light to how I can achieve that ?
Thank you in advance.
I figured it out. A many to one association was required. My initial thought was that one disability should have one description therefore there is a 1 : 1 relationship.
However, in reality, there are multiple disabilities that references the same description which means this is in fact a many to one relationship!
#Entity(name = 'STUDENT_DISABILITY')
class Disability {
#EmbeddedId
DisabilityId id
#Nullable
#Column(name = 'MOD_DT')
LocalDateTime modifiedDate
#ManyToOne
#JoinColumn(name = 'DISABILITY_ID, insertable = false, updatable = false)
DisabilityInfo disabilityInfo
}
Related
I'm programming a Multi-Tenant application. The tenant_id is present in every table and is part of each PK.
My tables are:
table S {
tenant_id PK
id PK
pid
...
}
table P {
tenant_id PK
id PK
...
}
Table S has a PK made up with (tenant_id, id). Table P has a PK made up with (tenant_id, id). Table S links to table P via (S.tenant_id = P.tenant_id and S.pid = P.id)
When I've implemented this in Java JPA, I can read table S including P, but adding a S has a strange behavior: JPA adds the missing P but doesn't fill S.pid so the P is correct but there is no link from S to P.
I don't want to have bidirectional links because P is also referred to by other fields of other tables.
My problem is somewhat similar to another question which has no answer yet.
The Java classes look like (simplified):
#Embeddable
public class MultiTenantId implements Serializable {
#Column(name = "tenant_id")
private String tenantId;
#Column(name = "id")
private String id;
...
}
#Entity
#Table(name = "P")
public class P {
#EmbeddedId
private MultiTenantId id;
...
}
#Entity
#Table(name = "S")
public class S {
#EmbeddedId
private MultiTenantId id;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "tenant_id", referencedColumnName = "tenant_id", insertable = false, updatable = false),
#JoinColumn(name = "pid", referencedColumnName = "id", insertable = false, updatable = false)}
)
private P p;
...
}
If I don't set the insertable = false, updatable = false then error
because the tenant_id is already used (in the MultiTenantId and in
this #ManyToOne)
if I set them then reading is OK but adding a S via JPA has a strange effect in the DB: it correctly adds the P, correctly adds the S except that it never sets the pid in the table S ==> the link is not done.
Thanks a lot in advance for your help, I'm completely lost with this problem !!
I want to join only one column from another table.
I have 2 entities now:
#Entity
public class Message {
....
#ManyToOne
#JoinColumn(name = "ATTRIBUTE_ID")
private Attribute attribute;
}
#Entity
#Table(name = "ATTRIBUTE_TABLE")
public class Attribute {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
}
And I want to simplify code and don't use entity for only one column:
#Entity
#SecondaryTable(name = "ATTRIBUTE_TABLE", pkJoinColumns =
#PrimaryKeyJoinColumn(name = "ID", referencedColumnName = "ATTRIBUTE_ID")),
public class Message {
....
#Column(table = "ATTRIBUTE_TABLE", name = "NAME")
private String attribute;
}
But #SecondaryTable JoinColumn cannot reference a non-primary key.
How to add a column from another table without using additional entity for it?
I have two entity classes.
Order.java
#Entity
#Table(name = "order_table")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, insertable=false, updatable=false)
private Set<Item> items;
// getters & setters & toString
Item.java
#Entity
#Table(name = "item")
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "order_id", nullable = false)
private Long orderId;
// getters & setters && toString
I created a test class like this:
#Test
public void createOrderWithItems() {
Item item = new Item();
item.setName("Iron Man");
Order order = new Order();
order.setName("Toy");
order.getItems().add(item);
Order created = service.createOrder(order);
Order orderById = service.getOrderById(order.getId());
System.out.println("Created Order: " + orderById);
Item itemById = service.getItemById(item.getId());
System.out.println("Created item: " + itemById);
Assert.notNull(created.getId(), "Order ID is Null");
}
Test is green but if you check output, you'll see that orderId field in the Item class is null.
Created Order: Order{id=1, name='Toy', items=[Item{id=2, name='Iron Man', orderId=null}]}
Created item: Item{id=2, name='Iron Man', orderId=null}
Does JPA not update this column in the db automatically? Is this column is redundant? If so, how can I retrieve this information from test code?
You need to set orderId explicitly.
item.setOrderId(order.getId());
order.getItems().add(item);
You can create a method addItem(Item item) in your Order class and hide this logic within it.
Cascading will create an entry in db but it won't initialize field. JPA annotations just indicate to JPA provider how to perform mapping between entity and table.
Moreover, check your annotations. #JoinColumn should be used in the entity which owns the relationship (the corresponding table has column as a foreign key). Check the top answer for this question for detailed explanations: What's the difference between #JoinColumn and mappedBy when using a JPA #OneToMany association
In my application every customer can have several accounts. I have the following data structure (a lot omitted for brevity):
#Entity
#Table(name = "CUSTOMER")
public class Customer {
#Id
#Column(length = 36, name = "CUSTOMER_ID", nullable = false, unique = true)
private String id;
#OneToMany
#JoinColumn(name = "OWNER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Account> accounts;
}
#Entity
#Table(name = "ACCOUNT")
public class Account {
#Id
#Column(length = 36, name = "ACCOUNT_ID", nullable = false, unique = true)
private String id;
#Column(name = "OWNER_ID", nullable = false)
private String ownerId;
}
If I use JPA to delete a Customer, such as
entityManager.remove(customer);
it tries to update the related ACCOUNT.OWNER_ID fields with null. OWNER_ID is not nullable, so it throws a JDBCException and rolls back the transaction.
What I need to achieve is that the related ACCOUNT rows get deleted (if any). How can I do that?
Thank you
Update: I tried it with
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
but it does not change the behavior: still tries to update with null.
I think you need to be using cascading in order to remove the child elements. Try this:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "OWNER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Account> accounts;
You should also reference the Customer in your account by a ManyToOne relationship and not the String id. I think this should solve your issue:
#Column(name = "OWNER_ID", nullable = false)
#ManyToOne
private Customer owner;
Ive added 2 hibernate model objects
First table
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#Column(name = "CA_ID", nullable = false, insertable = true,updatable = true, length = 22, precision = 0)
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "CSM_ACTIVITIES_SEQ")
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "activityId", cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
List<ActivitiesProductsMO> relatedProducts;
...getters / setters
}
The other table is
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
#Column(name = "CAP_ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "ACTIVITIES_PRODUCTS_SEQ")
private Long id;
#Column(name = "CAP_ACTIVITY_ID")
private Long activityId;
#Column(name = "CAP_PRODUCT_ID")
private Long productId;
...getters/setters
}
The point is to populate each db record for ActivitiesProductsMO.activityId with ActivityMO.id value
I.e.
If I create an activity record with id = 555
I'll get another activity_product record with activityId of 555
How can i get this to work?
Thank you!
Instead of manually trying to map the entitiy relations with long values you should use a bidirectional OneToMany relationship from ActivityMO to ActivitiesProductsMO
change ActivitiesProductsMO to:
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
// cut unimportant code ...
#ManyToOne
#JoinColumn(name = "CAP_ACTIVITY_ID")
private ActivityMO activityId;
// cut unimportant code ...
}
If you then were to persist an ActivityMO that already has ActivitiesProductsMO entries in its relatedProducts List, the Cascade type should actually take care and create those products while filling out the CAP_ACTIVITY_ID database field with the right value.
Another Possible Solution:
Use a Unidirectional OneToMany:
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "CAP_ACTIVITY_ID")
List<ActivitiesProductsMO> relatedProducts;
}
And remove the
private Long activityId;
from your ActivitiesProductsMO class.
This should both lead to identical database structure. But in the second case you would no longer have the "backlink" inside java from ActivitiesProductsMO to ActivityMO