I created a #ManyToMany relation between two table, without have a relational entity through them.
Something like this:
#Entity
public class Category{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CATEGORY_ID", unique = true, nullable = false)
private Long categoryId;
}
#Entity
public class Content {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CONTENT_ID", unique = true, nullable = false)
private Long contentId;
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(
name="CATEGORIES_CONTENTS",
joinColumns={
#JoinColumn(name="CONTENT_ID",
referencedColumnName="CONTENT_ID",
nullable = false)},
inverseJoinColumns={
#JoinColumn(name="CATEGORY_ID",
referencedColumnName="CATEGORY_ID",
nullable = false)
})
private List<Category> categories;
}
Now, after the end of the project, I have the need to add an extra field in the relational table to handle a new feature.
Is it possible to do that, without create the related entity? I don't want to change a lot of things.
I already read this good article: Can add extra field(s) to #ManyToMany Hibernate extra table?
but it isn't what I need.
Tanks a lot, Davide.
Related
I have 3 tables which are Person Login and Account.
Person and Login is OneToOne relation and Login has one FK which is connected Person's id column called PERSON_ID.
Person(one) and Account(many) is OneToMany relation and Account has one FK which is connected Person's id column called PERSON_ID as well .
what i want to do is when i delete one data from Account , nothing happen to Person and Login.
if i delete one data from Person which id=1, Login's PERSON_ID=1 data will be deleted , and all of the data PERSON_ID=1 from Account will be deleted as well.
if i delete one data from Login which PERSON_ID=1, Person 's id=1 data will be deleted , and all of the data PERSON_ID=1 from Account will be deleted as well.
how should i set the cascade ?
i've tried dozens of times and still can't find the logic in there, thanks!!
here's my code of all 3 tables without setting cascade:
`
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "ID", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "NAME")
private String name;
#Column(name = "SEX")
private String sex;
#OneToMany(mappedBy = "person",fetch = FetchType.LAZY)
private List<Account> account;
#OneToOne(mappedBy = "person")
private Login login;
#get..
#set..
}
`
#Entity
#Table(name = "ACCOUNT")
public class Account {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "ACCOUNT")
private String account;
#Column(name = "AMOUNT")
private String amount;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSON_ID",referencedColumnName = "ID")
public Person person;
#get..
#set..
}
`
#Entity
#Table(name = "LOGIN")
public class Login {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "USERNAME")
private String userName;
#Column(name = "PASSWORD")
private String password;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSON_ID", referencedColumnName = "ID")
private Person person;
#get..
#set..
}
It's been a while, but if I'm not mistaken you need to use the cascade=REMOVE option on the OneToMany and OneToOne relationships. In the OneToOne I think you need to specify cascade=REMOVE on the side that does NOT own the relationship, that is, the side that also contains the "mappedBy" property.
Finally, I believe JPA will NOT automatically load lazy relationships and then cascade them. I'm thinking you may need to fetch the relationship before you delete the parent entity (otherwise JPA will not know what to delete).
I would like to ignore #OnetoMany field in my entity. fetch data need to get actual fields but don't want to fire query to dependent table. But deleting data from parent table needs deletion from dependent table
I have tried #Transient that ignores but the delete is also being ignored. Is there any other option to tell JPA not to fetch data from childs table when i call the parent entity?
#Entity
Table(name = "User")
public class UserEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "SERIAL", unique = true, nullable = false)
private String serial;
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL)
private Set<UserActionEntity> userActionsById;
}
#Table(name = "user_action")
public class UserActionEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "action")
private String action;
#ManyToOne
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", nullable = false)
private UserEntity userByUserId;
If you don't want to fire query to dependent table, you can use (fetch = FetchType.LAZY) on UserActionEntity property.
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserActionEntity> userActionsById;
I have Spring Data Entity:
#Entity
#Table(name = "price_alert")
#Data
public class PriceAlert {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "customer_id")
private Long customerId;
}
So, for insert/update this entity perfectly fit for me: no need to load entity by id (here: for save PriceAlert it would be needed to load Customer).
But for some selects I also want to retrieve Customer. Currently I solve this problem by having 2 entities with 2 different corresponding repositories. In first entity all fields are plain Long values. In second I use Mapped value (like bellow).
#Entity
#Table(name = "price_alert")
#Data
public class PriceAlertExtended {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Customer customer;
}
With both #Column and #JoinColumn in entity on create I get SQLException: Field 'customer_id' doesn't have a default value
So my question is about: is there any better approach to this problem?
For your particular use case. I would suggest you to look at the following snippet
#Entity
#Table(name = "price_alert")
#Data
public class PriceAlertExtended {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Customer customer;
#Column(name = "customer_id", insertable = false, updatable = false, nullable = false)
private Long customerId;
}
Here, I have used fetch = FetchType.LAZY in #ManyToOne, this will save a JOIN on Customer Table until to access it by person.getCustomer().
And, I have created an extra field to access customerId to just get the customer_id. Make sure to mention nullable = false, insertable = false and updatable = false
I have 2 entities that have Id's annotated but those Id's aren the primary keys in the tables. I am still mapping the PK's in to the entity for now to limit the initial impact of the change. But the association table that uses the PK's to associate the many to many relationship is throwing the following error:
java.lang.IllegalArgumentException: Provided id of the wrong type for class. Expected: class java.util.UUID, got class java.lang.Long
The entity #Id is the UUID but the table PK which is a Long is mapped as the #JoinColumn
The composite key for the association entity
#Embeddable
public class CustomerAccountId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "user_id", nullable = false)
private Long customerId;
#Column(name = "account_id", nullable = false)
private Long accountId;
The association entity:
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "customerId", column = #Column(name = "user_id", nullable = false)),
#AttributeOverride(name = "accountId", column = #Column(name = "account_id", nullable = false))
})
private CustomerAccountId id;
#ManyToOne
#JoinColumn(name = "user_id", insertable = false, updatable = false, referencedColumnName = "user_id")
private Customer customer;
#ManyToOne
#JoinColumn(name = "account_id", insertable = false, updatable = false, referencedColumnName = "id")
private Account account;
The failing entity:
#Column(name = "user_id")
private Long serialId; // also the primary key in the table
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#org.hibernate.annotations.Type(type="pg-uuid")
#Column(name = "uid", updatable = false)
private UUID id;
Does anyone know if this is even possible? or am I going to be forced to update the content in the association table when I push this change?
put #Id and #GeneratedValue on the field that represent the table data then hibernate will map a long (sgbd) whit a long (table)
or
your (sgbd) table data type must be compatible with the (java) uuid type.
Why these 2 keys on your table?
I think it's not possible to have 2 PK for one entity. At most you can have a composite key base on your serialID and the UUID.
see How to map a composite key with Hibernate? for that
Or mark as #Id the real PK in the SGBD. Use the other in Java as a classic value in the table's point of view
The solution I decided to go with is the following:
#ManyToOne
#JoinColumns ( value = {
#JoinColumn(name = "user_id", insertable = false, updatable = false, referencedColumnName = "user_id"),
#JoinColumn(name = "user_uid", insertable = false, updatable = false, referencedColumnName = "uid")
})
private Customer customer;
In a nutshell I simply added the UUID to the association table and used both columns for the Customer Entity.
To address #Tokazio's question about using UUID and serial Id, the data warehouse conversion is impacted significantly so I need to slowly move from serial Id's to UUID's to minimize impacts and prevent outages.
I have an Entity that looks like this:
public class NpcTradeGood implements Serializable, Negotiabble {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected NpcTradeGoodPK npcTradeGoodPK;
#JoinColumn(name = "npc_id", referencedColumnName = "id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false)
private Npc npc;
}
#Embeddable
public class NpcTradeGoodPK implements Serializable {
#Basic(optional = false)
#Column(name = "npc_id", nullable = false)
private long npcId;
#Basic(optional = false)
#Column(name = "good_id", nullable = false)
private long goodId;
#Basic(optional = false)
#Column(name = "type", nullable = false)
#Enumerated(EnumType.STRING)
private ItemType type;
}
Is there a way to tell JPA which OneToMany relationship it is based on the type column (enumeration)?
Like if its a part or any other entity it automatically gets the related entity.
Thanks in advance.
In your PK object, you don't need to store the ids as longs (actually, this is true every time you need a reference to an entity). When mapping to the actual DB schema, JPA replaces all the references to other entities by thoes entities' ids.
So, if you use this (notice that I replaced your 'long' ids with actual references to the entities):
#Embeddable
public class NpcTradeGoodPK implements Serializable {
#ManyToOne
#JoinColumn(name = "npc_id", nullable = false)
private Npc npc;
#ManyToOne
#JoinColumn(name = "good_id", nullable = false)
private Good good;
#Column(name = "type", nullable = false)
#Enumerated(EnumType.STRING)
private ItemType type;
}
... JPA will map this to the DB, using: "long npc_id" where we refer to "Npc npc"; and "long good_id" where we refer to "Good good".
One important thing: you cannot use #Column with #ManyToOne. You may use #JoinColumn instead which will allow you to do the same things you do now.
Also, you don't need to specify all those 'optionals'. 'nullable' should take care of that.
Edited: ah, the Npc in NpcTradeGoodPK will probably collide with the Npc in the entity that embeds it. Consider renaming one of them.