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
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;
}
I am trying to establish a OneToOne relationship between two entities (PartnerDetails and JWTData. How ever, I only want to store the primary key of PartnerDetails entity in JWTData, not the whole object, like this.
#Entity
#Data
#Table(name = "partner_details")
public class PartnerDetails {
#Id
#Column(name = "partner_id")
private String partnerId;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "partnerId")
private JWTData jwtData;
}
#Entity
#Data
#Table(name = "jwt_data")
#NoArgsConstructor
public class JWTData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(targetEntity = PartnerDetails.class)
#JoinColumn(name = "partner_id", foreignKey = #ForeignKey(name = "fk_jwt_partnerdetails_partnerid"))
private String partnerId;
#NotBlank
private String secret;
}
But after fetching the JWTData using repository, Hibernate cannot convert the String to a PartnerDetails. Can this be done using any other way?
If you just add PartnerDetails to JWTData then JPA will know to use only the id. JPA is an Object Oriented framework so you should reference objects unless you specifically want a field. JPA handles the details for you. Note that in this configuration JWTData in the "owning" entity because of the mappedBy annotation, therefore only setting the partnerDetails field in a JWTData instance will persist the relationship to the database. The jwtData field in PartnerDetails is for query results only and makes for a Bidirectional instead of a Unidirectional mapping. Also, because of this, having a CascadeType setting generally only makes sense on the owning entity since it is the one handling the database updates and deletes.
When playing around with JPA be sure to turn on the SQL output so that you know what is actually happening.
#Entity
#Data
#Table(name = "partner_details")
public class PartnerDetails {
#Id
#Column(name = "partner_id")
private String partnerId;
#OneToOne(mappedBy = "partnerDetails")
private JWTData jwtData;
#Entity
#Data
#Table(name = "jwt_data")
#NoArgsConstructor
public class JWTData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// even though it looks like the entire class it's only saving the id to the database.
#OneToOne
private PartnerDetails partnerDetails;
In a db, I have User and Role entities. The share a many-to-many relation as a Role entity can be assigned to multiple User entities and on the other hand a User entity can be assigned to multiple Role entities.
My entity classes look like this
UserEntity
#Entity
public class UserEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(unique = true, nullable = false)
private String username;
#ManyToMany
private Set<RoleEntity> roles;
...
}
RoleEntity
#Entity
public class RoleEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(unique = true, nullable = false)
private String name;
#ManyToMany(mappedBy = "roles")
private Set<UserEntity> users;
...
}
With this configuration I am able to map the entities with each other. I am also able to delete a User entity. But I am not able to delete a Role entity as long as a relation exists.
If I add cascade = CascadeType.REMOVE the Rolegets deleted, but with it the User too of course.
To only way to get this working currently is to define a #JoinTable on both sides. But this seems more like a workaround. What am I doing wrong? As this is a regular use case, there got to be solution to this, although I haven found it yet...
You need the join table, it's not a work around. Remember you are mapping your object oriented model to a relational model. The only way to express many-to-many relationship in the relational model is defining a #JoinTable.
UPDATE: Adding comment in the answer
You sould define the #JoinTable just in one entity, for example UserEntity and mappedBy="roles" in RolesEntity inherits the definitions of #JoinColumn and #JoinTable names.
Then you need to define the cascade operations you want to perform in both sides of the relationship.
In RoleEntity
#ManyToMany(mappedBy = "roles")
private Set<UserEntity> users;
In UserEntity
#ManyToMany
#JoinTable(...)
private Set<RoleEntity> roles;
I have a concrete class for Employee entity. Employee is persisted by other application, I'm just using the data. I want to extend Employee to add properties that are other Entities using composition. I don't need to persist a child entity per se, but only the entities I'm trying to extend to Employee with using composition. Here is some code to help clear things up.
#Entity
#Table(name = "Legacy_Table_Name", schema = "another_owner")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "emp_id")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String emp_id;
private String firstName;
etc...
-
public class EnhancedEmployee extends Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Transient
private SomeEntity someCompositionProperty;
#OneToMany(mappedBy = "employee", fetch = FetchType.EAGER, cascade=CascadeType.ALL)
private Collection<AnotherEntityWithItsOwnTable1> list1;
#OneToMany(mappedBy = "employee", fetch = FetchType.EAGER, cascade=CascadeType.ALL)
private Collection<AnotherEntityWithItsOwnTable2> list2;
If I make EnhancedEmployee an entity then JPA tries to create/use an EnhancedEmployee database table (right now in dev so I'm using create-drop persistence.xml property). If I remove the #Entity annotation from EnhancedEmployee, JPA complains that EnhancedEmployee is not an Entity in other classes where I want to utilize these extra properties
#ManyToMany(cascade = CascadeType.MERGE)
#LazyCollection(LazyCollectionOption.FALSE)
#JoinTable(name = "PARTICIPATING_EMPLOYEES", joinColumns = { #JoinColumn(name = "event_id") }, inverseJoinColumns = { #JoinColumn(name = "emp_id") })
private Collection<EnhancedEmployee > participants;
All I'm trying to do is reference an Employee setter getters for these extra properties whether or not they have data persisted for these extra properties.
I realize I could probably just modify my Employee class and add someCompositionProperty and list1/2 relations to that class but doesn't that then violate https://en.wikipedia.org/wiki/Open/closed_principle. While my Employee entity class is the "same" for all my projects the source code is really part of each project's package so perhaps the open/closed doesn't apply here and I should just modify the Employee entity class
If I make EnhancedEmployee an entity then JPA tries to create/use an
EnhancedEmployee database table (right now in dev so I'm using
create-drop persistence.xml property).
You could avoid this by changing the inheritance type to SINGLE_TABLE
Also your discriminator column emp_id seems to me like a bad choice. Better change it to something like #DiscriminatorColumn(name = "TYPE") because emp_id is the primary key and and cannot repeat itself in a table. Also your EnhancedEmployee needs #DiscriminatorValue(value = "ENHANCED")