I have two simple entities:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
#Entity
public class Address {
#Id
#OneToOne
#JoinColumn(name = "FK")
User user;
}
When I run my Spring Boot application I get the following error:
org.hibernate.MappingException: Composite-id class must implement Serializable: Address
But the entities are almost identical to those in JPA Spec (Section 2.4.1, Example 4). For some reason Hibernate thinks that the user attribute is a composite id.
What am I doing wrong?
I would suggest you to correct your mapping in this way:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
#Entity
public class Address {
#Id
Long id;
#MapsId
#OneToOne
#JoinColumn(name = "FK")
User user;
}
See also this section of hibernate documentation.
it seems you are using #Id two times in one Entity class
Related
I have a one-to-many relationship with Customer and Address, but I'm not sure how to represent this with JPA. I don't want use #OneToMany for the AddressEntity's in CustomerEntity because I want to avoid wrapping it in a Collection.
I'm wondering what annotation or even other strategies I can use to maintain the relationship where one customer will, for simplicity, always have two addresses. Any suggestions are appreciated!
Address Entity
#Data
#NoArgsConstructor
#Entity(name = "address")
public class AddressEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#?
private CustomerEntity customer;
}
Customer Entity
#Data
#NoArgsConstructor
#Entity(name = "customer")
public class CustomerEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#?
private AddressEntity shippingAddress;
#?
private AddressEntity billingAddress;
}
For the case when an address can belong different customers.
#Entity
public class AddressEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
#Entity
public class CustomerEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private AddressEntity shippingAddress;
#ManyToOne
private AddressEntity billingAddress;
}
if each customer has unique address, better to store the addresses in the same customer record.
You can create class EmbeddedAddress and use #Embedded and #Embeddable annotations.
For your exact scenario, I think you could go for #PostLoad.
The steps would be:
use #OneToMany annotation to load the addresses into a collection
annotate both shippingAddress and billingAddress with #Transient
create a public method annotated with #PostLoad
initialise your 2 transient fields (you need to have at least an enum to discriminate between the addresses)
Why would the steps above work?
PostLoad is invoked after an entity is loaded from the database
the fields need to be transient, because they are not mapped to database columns
A relevant example can be found here.
While the approach above would solve your problem, it adds some degree of verbosity in your JPA entities. I would suggest to go for #OneToMany and make sure you add an enum in AddressEntity to check if an address is for shipping or billing.
Also, given that you mentioned that there is a one-to-many relationship between a customer and an address, then there is a many-to-one relationship between an address and a customer. The annotation to use in the AddressEntity class is #ManyToOne
Can someone please explain to me how to declare annotations on entities correctly?
There are two tables in the database. One table has a foreign key to a id of another one (#ManyToOne binding), both IDs are of Integer. And I created two entities to represent them in code.
How can I map these entities to DB? I had realized recently that Hibernate refers to classes. I mean that in #JoinColumn("???") I have to write the name of a column in DB, isn't it? What if the ID field's name in the entity is the same as in the DB? Thanks everyone in advance! Regards.
#Entity
#Table(name = "stat")
public class Statistic {
#Id
#Column( name = "statisticId")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statisticsId;
...............
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn (name="userId") //<------------ The same name.
private User userStat;
//getters and setters + constr + overriding of ToString()
}
#Entity
#Table(name = "usser")
public class User {
#Id
#Column(name = "userId")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;`enter code here`
................
#OneToOne(mappedBy = "userStat")
private Statistic statisticUser;
}
Have next tables structure in SQL schema :Clients, Employees, Orders.
And 3 Entity classes in java code accordingly : Client, Employee, Order.
Both primary id fields from Clients and Employees are in Orders table as foreign keys.
Question is how it should be displayed in java code?
As I understand here it should be done smth like adding Set field to Clients and Employees annotated with #OneToMany.
But what should be done in Order Entity and maybe I have to add any additional annotations except #OneToMany?
I think you have some misconceptions about the relational mapping of Hibernate.
If in fact your Orders table have foreign keys of Clients and Employees, then the annotation you are looking for is #ManyToOne
#OneToMany annotation is used when your entity have multiple records referenced by the targeted entity, while #ManyToOne is used when your entity have only one record referencing the targeted entity.
For example:
Orders entity have one reference from Clients and one reference from Employees entities.
In this case, Orders entity could be mapped by the following way:
#Entity
#Table(name = "Orders")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Client client;
#ManyToOne
private Employee employee;
//getters and setters
}
#Entity
#Table(name = "Clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String attribute1;
//getters and setters
}
#Entity
#Table(name = "Employees")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String attribute1;
//getters and setters
}
With the example given above you should be able to make your schema work fine with Hibernate, but for the sake of understanding, let's imagine a scenario where you would need to get all the Orders from a Client, of course you could do it with a query selecting only the Orders inside the Client table, however Hibernate offers the #OneToMany annotation which will give you the possibility to access all the Orders from a Client without the need of a separate query, only by mapping! Let's see an example:
#Entity
#Table(name = "Orders")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Client client;
//getters and setters
}
#Entity
#Table(name = "Clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String attribute1;
#OneToMany(mappedBy = "client")
private List<Order> orders;
//getters and setters
}
In this example you should be able to get all the Orders from a Client just by calling the get of the "orders" attribute. Please, note that on the #OneToMany mapping we have specified the "mappedBy" attribute as "client", it was needed because we have a bidirectional mapping between Client and Order, in a simple usage of #OneToMany you would not need this mapping.
Important: When working with #OneToMany mapping you would eventually face some lazy fetching problems, in this case I highly recommend you to take a look at this question:
Solve “failed to lazily initialize a collection of role” exception
Also, I think you should start reading more about Hibernate to understand about it's basic concepts, please, check this other question about #OneToMany and #ManyToOne annotations on Hibernate:
Hibernate/JPA ManyToOne vs OneToMany
This is an extract of the JPA entities schema
#Entity
#Table(name="customer")
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="customer_id")
private Long id;
#ManyToOne
#JoinColumn(name="company_id",nullable=false)
private Company company;
//...
}
#Entity
#Table(name="company")
public class Company implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="company_id")
private Long id;
//...
}
#Entity
#Table(name="order_header")
public class OrderHeader implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="order_id")
private Long id;
#ManyToOne
#JoinColumn(name="customer_id",nullable=false)
private Customer customer;
private Long internalCompanyOrderId;
//...
}
I need to retreive an unique internalCompanyOrderId sequence for each Company_id. It means that nevertheless the Order PK will be 'order_id', I need an "internal" order_id for each Company.
There is no occurrences of #TableGenerator in your code sample. I assume you want to generate value with #GeneratedValue.
What it comes to JPA 2.0, according specification you cannot use #GeneratedValue for field that is not part of the primary key:
The GeneratedValue annotation may be applied to a primary key property
or field of an entity or mapped superclass in conjunction with the Id
annotation. [97]
...
[97] Portable applications should not use the
GeneratedValue annotation on other persistent fields or properties.
I want to use mixed #Inheritance strategy, but Hibernate doesn't support it.
Is there any way to implement JOINED inheritance without actual class inheritance.
For example:
#Entity
#Table(name="A")
#Inheritance(strategy=InheritanceType.JOINED)
public class A {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_SEQ")
private Long id;
//getters
//setters
}
#Entity
#Table(name="B")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class B {
#Id
private Long id;
//getters
//setters
}
So, basically in B I just want to refer to #Id generated in A without extending from A.
I found the solution. JPA doesn't allow you to combine #Id and #OneToOne. However, #MapsId annotation does the trick:
#Entity
public class A {
#Id
private Long id;
//getters
//setters
}
#Entity
public class B {
#Id
private Long id;
#MapsId
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(nullable=false, name="id")
private A a;
//getters
//setters
}
I think you can accomplish this by making a #OneToOne relationship or a #OneToMany and point the table name like this
#Id #OneToOne
#JoinColumn(name = "id")
private A a;