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.
Related
I have two entities:
#Entity
#Table(name = "entity_a")
#Audited
public class EntityA {
#Column(name = "entity_a_uuid", columnDefinition = "char", updatable = false)
#Type(type = "uuid-char")
private UUID uuid = UUID.randomUUID();
/**
* #deprecated in favor of uuid
*/
#Deprecated
#Id
#GeneratedValue
#Column(name = "entity_a_id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "entity_a_id", nullable = false)
#BatchSize(size = 100)
#NotAudited
private List<EntityB> entityBs = new ArrayList<>();
}
and
#Entity
#Audited
#Table(name = "entity_b")
public class EntityB {
#Id
#Column(name = "entity_b_uuid", columnDefinition = "char", updatable = false)
#Type(type = "uuid-char")
private UUID uuid = UUID.randomUUID();
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "entity_a_id", nullable = false, insertable = false, updatable = false)
private EntityA entityA;
}
Each is correctly audited into two tables entity_a_audit and entity_b_audit. However, the entity_a_id field in entity_b_audit is always null.
Some details:
If I do not have the #NotAudited in EntityA, I will get an error that says something to the effect of: The table EntityA_EntityB_audit does not exist. This seems like it's trying to audit them as a single table, which I do not want.
I have tried applying #Audited(targetAuditMode = elationTargetAuditMode.NOT_AUDITED) to each side. If applied only in EntityA, I get the above error. If applied only in EntityB, nothing changes. If applied in both, I get the error above. If applied to neither, I get the error above.
I suspect the entity_a_id is null in entity_b_audit because the id isn't generated until EntityA hits the DB. entity_a_id is auto-incrementing in the entity_a table.
Using hibernate-envers-5.4.32.Final.jar
Ultimately, I would like for entity_a_id to not be null in entity_b_audit. Alternatively, if I could somehow get entity_a_uuid to be captured instead, that would also suffice.
Any help would be greatly appreciated! Thanks.
You marked the column as insertable = false, updatable = false, so there is nothing to audit here, because Hibernate can never change the value of that column.
I have an old database where can't change the schema so I have to adapt my code to work.
Within that db I have two tables which I'd like to join.
The main Table called SFTSEB2 has a 5 Column Composite primary key: empfkey, ebmsg, ebbord, ebsort, ebrefn only these will going to fulfill the uniqueness on that Table.
I'd like to join SFTSEB3 to SFTSEB2 but based on only 4 Columns: empfkey, ebmsg, ebbord, ebsort
Entites:
#Entity
#Table(name = "SFTSEB2")
#IdClass(Sftseb2ID.class)
#Immutable
public class Sftseb2 {
#Id
private String empfkey;
#Id
private String ebmsg;
#Id
private String ebbord;
#Id
private int ebsort;
private int ebbpos;
private String ebsdgn;
#Id
private String ebrefn;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "empfkey", referencedColumnName = "empfkey", insertable = false, updatable = false),
#JoinColumn(name = "ebmsg", referencedColumnName = "ebmsg", insertable = false, updatable = false),
#JoinColumn(name = "ebbord", referencedColumnName = "ebbord", insertable = false, updatable = false),
#JoinColumn(name = "ebsort", referencedColumnName = "ebsort", insertable = false, updatable = false)
})
List<Sftseb3> sftseb3;
//GettersSetters
...
}
#Entity
#Table(name = "SFTSEB3")
#IdClass(Sftseb3ID.class)
#Immutable
public class Sftseb3 {
#Id
private String empfkey;
#Id
private String ebmsg;
#Id
private String ebbord;
#Id
private int ebsort;
#Id
private int ebsrt2;
#Id
private String ebsdgn;
private int ebbpos;
private String ebstst;
private String ebstsc;
//GettersSetters
//...
}
Unfortunately I can get past this because hibernate is giving back:
A Foreign key refering Sftseb2 from Sftseb3 has the wrong number of column. should be 5
I tried to have a bi-directional mapping so the complete JoinColumns part went to Sftseb3 with a #ManyToOne and Sftseb2 would only contain the mappedBy at the #OneToMany but I got the same issue.
As I see there is no way to have different PK on an entity and Join another table to it based on part of the PK or even other columns? Why?
If I reduce the number of #Id columns to match in the two tables so Sftseb2's #Ids: empfkey, ebmsg, ebbord, ebsort then it starts to work, however because this is non-unique all instances of Sftseb2 will be the same...
Let's say I have these Entities
Company,User,Project and Recommendation
But what I want to highlight is the Project and Recommendation table.
Below is the entity sample.
//Project entity
#Column(name = "id")
private String id;
#Column(name = "user_id")
private String userId;
#Column(name = "company_id", nullable = true)
private String companyId;
//Recommendation entity
#Column(name = "id")
private String id;
#Column(name = "user_id")
private String userId;
#Column(name = "company_id", nullable = true)
private String companyId;
#OneToMany(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "user_id", nullable = true, insertable = false, updatable = false),
#JoinColumn(name = "id", referencedColumnName = "company_id", nullable = true, insertable = false, updatable = false)
})
private List<Project> projects;
Above tables are just illustration, sorry if that doesn't really make sense.
I want to join Project table inside Certification table.
They both are not directly related, so I need to join using these 2 columns, userId and companyId.
However, companyId is nullable. And whenever it is null, I still want to join the table only by userId.
What I want to achieve is probably along this line:
Select from Recommendation rec Join Project project
on rec.user_id = project.user_id and (rec.company_id = project.company_id OR rec.company_id is null)
Is it possible to use hibernate / JPA annotations to achieve this?
My project uses Hibernate 5 recently
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 an ER relationship from a legacy DB (MS SQL Server based as below
The way that I'm currently trying to convert this to the JPA 2.1 style is as below
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Orders implements Serializable {
#Id
#GeneratedValue
#Column(name = "OrderNumber", nullable = false)
private Integer orderNumber;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "OrderHistory",
joinColumns = {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false)
}
)
private List<OrderHistory> orderHistory;
----Other properties, Getters and Setters
}
#Entity
#PrimaryKeyJoinColumn(name = "OrderNumber")
public class SpecialOrders extends Orders implements Serializable {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false)
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#Fetch(FetchMode.SELECT)
#OrderBy("sequenceNumber ASC")
private List<Items> items;
----Other properties, Getters and Setters
}
#Entity
#IdClass(ItemsPK.class)
public class Items implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "CustomOptions",
joinColumns = {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false),
#JoinColumn(name = "SequenceNumber", referencedColumnName = "SequenceNumber", nullable = false)
}
)
private List<CustomOptions> customOptions;
----Other properties, Getters and Setters
}
#Entity
public class ItemsPK implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
----Getters and Setters
}
#Entity
#IdClass(CustomOptionsPK.class)
public class CustomOptions implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#Id
#Column(name = "OptionNumber", nullable = false)
private Integer optionNumber;
----Other properties, Getters and Setters
}
public class CustomOptionsPK implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#Id
#Column(name = "OptionNumber", nullable = false)
private Integer optionNumber;
----Getters and Setters
}
With the above code, I see that hibernate does below
INSERTs into Orders and gets the GeneratedId for OrderNumber
INSERTs into SpecialOrders, using the orderNumber retrieved above.
Attemps to INSERT into Items table, with a NULL value in the orderNumber and then fails because the orderNumber is a NOT NULL column.
Subsequently, If add a "Simple" primary key to Items table and make the orderNumber as a NULLable column, then the below happens:
INSERTs into Orders and gets the GeneratedId for OrderNumber
INSERTs into SpecialOrders, using the orderNumber retrieved above.
INSERTs into Items table with orderNumber as NULL value and gets the generated id of the Items table row.
Updates the row of Items table with the orderNumber from parent, using the retrieved id for Items.
Attemps to INSERT into CustomOptions table, with a NULL value in the orderNumber and then fails because the orderNumber is a NOT NULL column.
As per the above sequence, it seems that:
Composite Primary key doesnt seem to be working correctly or not supported.
Hibernate is handling the OneToMany relationship inefficiently by issuing an INSERT followed by an UPDATE, instead of just an insert.
Any idea if my understanding is correct? The only way of fixing this issue seems to be that I need to remove the composite primary key and replace it with a simple one.