I'm using Spring(xml+annotations), Hibernate(annotations) in this web service project. The database relationship diagram, models, expected and actual output are given below,
Database Table relationship
Customer.java
#Entity
#Table(name="customer")
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="customer_id", unique=true, nullable =false)
long customerId;
#Column(name="name")
String name;
#Column(name="secondary_name")
String secondaryName;
#Column(name="date")
Date date;
#Column(name="address")
String address;
#Column(name="post")
String post;
#Column(name="pin")
String pin;
#Column(name="phone")
String phone;
#OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade=CascadeType.ALL)
#JsonManagedReference
Set<Loan> loans = new HashSet<Loan>();
//constructors, getters and setters
}
Loan.java
public class Loan implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="loan_id", nullable=false, unique=true)
long loanId;
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="customer_id", nullable = false)
#JsonBackReference
Customer customer;
#Column(name="date", nullable=false)
Date date;
#Column(name="amount", nullable=false)
double amount;
#OneToMany(fetch=FetchType.LAZY, mappedBy="loan", cascade=CascadeType.ALL)
#JsonManagedReference
List<Item> items = new ArrayList<Item>();
//constructors, getters, setters
}
Item.java
public class Item implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="item_id", nullable=false, unique=true)
long itemId;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="loan_id", nullable = false)
#JsonBackReference
Loan loan;
#Column(name="name", nullable=false)
String name;
#Column(name="weight", nullable=false)
double weight;
//constructors, setters, getters
}
Actual output:Here, customer details are not shown
{
"loanId":4,
"date":1484937000000,
"amount":10000.0,
"items":[
{
"itemId":3,
"name":"Item1",
"weight":10.0
},
{
"itemId":4,
"name":"Item2",
"weight":20.0
}
]
}
Expected output: need to display customer details also when looking for a loan
{
"loanId":4,
"customer":{
"customerId":2,
"name":"Prem",
"address":"Street,State"
},
"date":1484937000000,
"amount":10000.0,
"items":[
{
"itemId":3,
"name":"Item1",
"weight":10.0
},
{
"itemId":4,
"name":"Item2",
"weight":20.0
}
]
}
I can able to fetch the customer details from the database and fail to load it using Jackson Json.
If I remove #JsonManagedReference, I end up with circular loop.
If I remove #JsonBackReference, no effects in the output.
Complete code at: https://github.com/liwevire/TM_Service
Thanks in advance.
Because you are using the #JsonBackReference on the Customer property in the Loan entity, the Customer object will not included in the serialization. Use the #JsonManagedReference for the Customer in the Loan object and use #JsonBackReference on the Loan property in the Customer entity.
This will serialize the Customer property of your Loan entity. But the Customer object serialization will not contains the Loan property. You need to pick one side of the relationship to serialize.
To allow both side, use #JsonIdentityInfo annotation in your entity and remove the #JsonBackReference and #JsonManagedReference. You entities will be something like:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "customerId")
public class Customer implements Serializable {
...
}
The property of the #JsonIdentityInfo refer to your entity id property, for Customer this will be customerId. Do this for Loan and Item also.
This seems pretty old but let me put my coins here as well; I would seperate the entity and model. Means;
> Client <-> Application : Models
>
> Application <-> Database : Entities
And your service layer or whatever layer you process data should make the conversion between entities and models.
You get rid of recursion by returning data as your wish.
You split the definitions between two different communication channels. This way you can decide what to show to your client and how to show your client as well. This will save your DB schema be exposed directly too.
You can extend model as per your wish without touching to the DB backend.
Here is a bit lengthy approach. But needs some design modifications in your app.
I had a similar problem and I created separate pojos for every entity classes.
In service layer I use these pojos instead of the entity objects as parameters and I use appropriate getters/setters to set/get the properties to/from entity classes.
In this way you can get/set the properties you want to and avoid unwanted ones. However I implemented additional methods in DAO layer to get the related entities. This is very lengthy approach but solved the problem for me.
Related
I think I have a bad setup for my hibernate database. I have Citizen entities who have one to many relationships with WeeklyCare entities. Below is the relevant code.
Citizen:
#Entity
#Table(name = "citizens")
public class Citizen {
#Id
#Size(max = 10, min = 10, message = "CPR must be exactly 10 characters")
private String cpr;
#OneToMany()
#JoinColumn(name = "cpr")
private List<WeeklyCare> weeklyCare;
}
WeeklyCare:
#Entity
public class WeeklyCare {
#EmbeddedId
private WeeklyCareIdentifier weeklyCareIdentifier;
}
WeeklyCareIdentifier:
#Embeddable
public class WeeklyCareIdentifier implements Serializable {
#NotNull
#Size(max = 10, min = 10, message = "CPR must tbe exactly 10 characters")
private String cpr;
#NotNull
private Integer week;
#NotNull
private Integer year;
}
I have some problems when I want to save data to the database:
I can't save WeeklyCare first, because it requires a Citizen.
When I send the citizens to my backend, the objects contain a list of WeeklyCare. When I try to save the citizens, it gives me this error: Unable to find Application.Models.WeeklyCare with id Application.Models.WeeklyCareIdentifier#b23ef67b
I can solve the problem by clearing the list of WeeklyCare on the Citizen before saving it, and then saving the list of WeeklyCare after, but that feels like a terrible way to do it.
I guess I want hibernate to ignore the list of WeeklyCare when it saves a Citizen, but acknowledge it when it fetches a Citizen. Is this possible? Or is there an even better way to do it? Thanks.
I can't save WeeklyCare first, because it requires a Citizen.
You have the "cpr" identifier used in two entities:
it's the primary Id for Citizen
it's part of the composite Id for WeeklyCare
You could, theoretically speaking, create a list of WeeklyCare (not with the way it is modeled now though) and later update the associations of each WeeklyCare to Citizen.
When I send the citizens to my backend, the objects contain a list of WeeklyCare. When I try to save the citizens, it gives me this
error: Unable to find Application.Models.WeeklyCare with id
Application.Models.WeeklyCareIdentifier#b23ef67b
The best way to map One-To-Many association is bidirectional. This will also save you from some unnecessary queries Hibernate is generating when using #OneToMany with #JoinColumn only.
1) Remove cpr from WeeklyCareIdentifier class (and probably rename the class).
#Embeddable
public class WeeklyCareIdentifier implements Serializable {
#NotNull
private Integer week;
#NotNull
private Integer year;
//constructors, getters, setters
}
2) Remove the composite #EmbeddedId in favor of Long id field:
#Entity
public class WeeklyCare {
#Id
#GeneratedValue
private Long id;
#Embedded
private WeeklyCareIdentifier weeklyCareIdentifier;
//constructors, getters, setters
}
3) Move to bidirectional mapping:
#Entity
#Table(name = "citizens")
public class Citizen {
#Id
#Size(max = 10, min = 10, message = "CPR must be exactly 10 characters")
private String cpr;
#OneToMany(
mappedBy = "citizen",
cascade = CascadeType.ALL, //cascade all operations to children
orphanRemoval = true //remove orphaned WeeklyCare if they don't have associated Citizen
)
private List<WeeklyCare> weeklyCares = new ArrayList<>(); //init collections to avoid nulls
//constructors, getters, setters
//add utility methods to manipulate the relationship
public void addWeeklyCare(WeeklyCare weeklyCare) {
weeklyCares.add(weeklyCare);
weeklyCare.setCitizen(this);
}
public void removeWeeklyCare(WeeklyCare weeklyCare) {
weeklyCares.remove(weeklyCare);
weeklyCare.setCitizen(null);
}
}
and:
#Entity
public class WeeklyCare {
#Id
#GeneratedValue
private Long id;
//having reference to the citizen entity from WeeklyCare
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "citizen_cpr")
private Citizen citizen;
#Embedded
private WeeklyCareIdentifier weeklyCareIdentifier;
//constructors, getters, setters
}
I would also recommend to use Long ids for the entities, even if the cpr is unique and so on. Convert the cpr to a normal column and introduce a DB generated ID column which you use in to join with in your internal domain and treat the cpr as a pure user-facing data column.
I am working on a REST based JAVA project using Spring framework. Some of my JPA entities have #JsonIgnore annotation from jackson for the properties. Example below.
#Entity
#Table(name="TRANSACTION")
public class Transaction {
#Id
#Column(name="SERIAL", length=16)
private String serial;
#Column(name="NARRATION", length=128)
private String narration;
#OneToMany(mappedBy="transaction", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<TransactionDetail> details;
// other fields goes here
}
#Entity
#Table(name="TRANSACTION_DETAIL")
public class TransactionDetail {
#Id
#Column(name="UUID", updatable=false)
private String uuid;
#JsonIgnore
#ManyToOne
#JoinColumn(name="FK_TRANSACTION")
private Transaction transaction;
#Column(name="ACCOUNT_NO")
private int accountNo;
// other fields goes here
}
As per the above class relationship, I want the #JsonIgnore to be applied only when I fetch Transactions.
List<Transactions> transactions = transactionsRepo.findAll()
But If I am directly fetching the TransactionDetails, I need the property transaction in TransactionDetail to be fetched as well.
List<TransactionDetail> details = transactionDetailsRepo.findByAccountNo(accountNo);
How can I use the #JsonIgnore to do this? Or else is there any other proper way to achieve this?
#Entity
#JsonInclude(JsonInclude.Include.NON_NULL)
#Table(name="TRANSACTION_DETAIL")
public class TransactionDetail {
#Id
#Column(name="UUID", updatable=false)
private String uuid;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="FK_TRANSACTION")
private Transaction transaction;
#Column(name="ACCOUNT_NO")
private int accountNo;
// other fields goes here
}
You can try using fetchtype as lazy , what this does is it will fetch the property in Transaction Detail Entity. but it will not fetch the related data of transaction_detail which means all of the other ones will be null. you can annotate your entity with #JsonInclude as I did to include only non null values.
and please add jackson-datatype-hibernate Jar files or if you are using maven add the dependencies in pom file
So in this example scenario I have an attendance DTO, and a worker DTO, workers in this context are separated by department, and a worker can only ever be inside of one department. It is important to note that Worker {id='123-123-123', department='a'} is different to Worker {id='123-123-123', department='b'}, despite them both sharing the same Id.
I have the following class setup to try and separate functions by id and department
public class IdAndDepartmentPK implements Serializable {
private String id;
private String department;
public IdAndDepartmentPK() {}
...
}
This key class is shared between DTOs that require both the Worker's id and department, below are the two DTOs that are causing a problem.
#Entity
#IdClass(IdAndDepartmentPK.class)
public class AttendencetDto {
#Id private String id; // This is a departmentally unique attendenceId
#Id private String department;
#Column private String workerId;
#JoinColumns({
#JoinColumn(name = "workerId"),
#JoinColumn(name = "department")
})
#ManyToOne(fetch = FetchType.EAGER)
private WorkerDto workerDto;
....
}
#Entity
#IdClass(IdAndDepartmentPK.class)
public class WorkerDto {
#Id
private String id;
#Id
private String department;
...
}
WorkerDto does not need to have knowledge of AttendencetDto, but AttendencetDto, does need to have access to WorkerDto and the other data it contains.
Hibernate complains that fields like workerId should be mapped with insert="false" update="false", but if I was to do this then I wouldn't be able to persist those values to the database.
I essentially want to have those fields available whilst also having the WorkerDto available, is this possible?
You should remove #Column private String workerId; because you already map it by relation to WorkerDto.
If you want to create relation between that you should use setWorkerDto method in your AttendencetDto and just save. After transaction ends you will have your relation in DB.
I have been trying to solve this for whole day but no luck! Also i tried to read most of the tutorials on the net but as you all know they all are full of useless examples that do not reflect what you need in the real world.
So here is my situation:
The database:
table: vehicles(vehicleId, brand, model, devYear, regNumber) <-- vehicleId is the PrimaryKey
table: extras(vehicleId, allowSmoke, allowFood, allowDrinks, airConditioner) <-- vehicleId is a PK and a FK.
The point is that if i have a class Vehicle and a class TravelExtras which are mapped to the database i want the Vehicle class to have an attribute TravelExtras travelExtras and get and set methods.
Unfortunatelly no matter what i tried when i try to persist the object in the databse i get various errors.
Here is an illustration:
EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "NaStopPU" );
EntityManager entitymanager = emfactory.createEntityManager( );
entitymanager.getTransaction( ).begin( );
TravelExtra travelExtra = new TravelExtra();
entitymanager.persist(travelExtra);
Vehicle vehicle = new Vehicle(2L, "10152487958556242", "Mazda", "626", "334343", 2005, 4);
vehicle.setTravelExtra(travelExtra);
entitymanager.persist(vehicle);
entitymanager.getTransaction().commit();
entitymanager.close( );
emfactory.close( );
Any one knows what kind of annotations to use for this One to one case ?
The Java Persistence wikibook has a section called Primary Keys through OneToOne and ManyToOne Relationships which seems to indicate that what you want is possible.
If I'm reading it right, for your case, it would look something like:
class Vehicle {
#Id
#Column(name = "EXTRAS_ID")
private long extrasId;
#OneToOne(mappedBy="vehicle", cascade=CascadeType.ALL)
private TravelExtra extras;
}
class TravelExtras {
#Id
#Column(name = "VEHICLE_ID")
private long vehicleId;
#OneToOne
#PrimaryKeyJoinColumn(name="VEHICLE_ID", referencedColumnName="EXTRAS_ID")
private Vehicle vehicle;
public TravelExtras(Vehicle vehicle) {
this.vehicleId = vehicle.getId();
this.vehicle = vehicle;
}
}
Note that one of your entities will need to make sure it has the same id as the other, which is accomplished in the example by the TravelExtras constructor requiring the Vehicle it is bound to.
I know this is very old qs, but for completeness of your case
you can just have (jpa 2.0)
#Entity
#Data
public class Vehicle implements Serializable{
#Id
#GeneratedValue
private long vehicleId;
.. //other props
}
#Entity
#Data
public class VehicleExtras implements Serializable{
#Id
#OneToOne (cascade = CASCADE.ALL)
#MapsId
#JoinColumn(name ="vehicleId")
private Vehicle vehicle;
#Column
private boolean allowSmoke;
..// other props.
}
should share same pk/fk for VehicleExtra table
Why don't you use an #Embedded object? When using an embedded object, you get
the logical separation you desire in your code and keep your database compliant with Entity-Relational Normalization rules.
It's weird to think on a One-to-One relationship, because even though JPA/Hibernate allows it, all data should be stored in the same table, making you model simpler, while also simplifying queries and increasing database performance by removing the need for a Join operation.
When using Embedded objects you don't have to worry about mapping IDs and bizarre relations, since your ORM is capable of understanding that your just making a code separation, instead of demanding an actual relation of One-to-One between tables.
class Vehicle {
#Id
#Column(name = "ID")
private long vehicleId;
#Column(name = "BRAND")
private String brand;
#Column(name = "MODEL")
private String model;
#Column(name = "DEV_YEAR")
private int devYear;
#Column(name = "REG_NUMBER")
private int regNumber;
#Embedded
private TravelExtra extras;
// Constructor, getters and setters...
}
.
#Embeddable
class TravelExtras {
#Column(name = "ALLOW_SMOKE")
private boolean allowSmoke;
#Column(name = "ALLOW_FOOD")
private boolean allowFood;
#Column(name = "ALLOW_DRINKS")
private boolean allowDrinks;
#Column(name = "AIR_CONDITIONER")
private boolean airConditioner;
// Default Constructor, getters and setters...
}
You can map your classes for example with Netbeans. It will generate annotations. The problem could be your dao layer. You have to persist objects in correct way. For example can't save travelExtra without Vehicle. Also be aware of owning side.
I am trying to figure out the best way to accomplish a relationship in hibernate. I have a Customer object. Each customer has a technical contact, a billing contact, and a sales contact. Each type of contact has the exact same data structure (phone, email, address, etc).
My first thought was to create a Contact table, and then have three columns in the Customer table - sales_contact, billing_contact, technical_contact. That would make three distinct foreign key one-to-one relationships between the same two tables. However, I have found that this is very difficult to map in Hibernate, at least using annotations.
Another thought was to make it a many to many relationship, and have a type flag in the mapping table. So, any Customer can have multiple Contacts (though no more than three, in this case) and any Contact can belong to multiple Customers. I was not sure how to map that one either, though. Would tere be a type field on the map table? Would this attribute show up on the Contact java model object? Would the Customer model have a Set of Contact objects. or three different individual Contact objects?
So I am really looking for two things here - 1. What is the best way to implement this in the database, and 2. How do I make Hibernate map that using annotations?
It can be as simple as :
#Entity
public class Contact {
#Id
private String id;
private String phome;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#ManyToOne
#JoinColumn(name = "ID")
private Contact billingContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact salesContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact technicalContact;
public Customer() {
}
// ... Getters and Setters
}
Now, if you want to make the difference between a BillingContact and a SalesContact at the object level, you can make Contact abstract, and implement it with each type of contact. You will have to annotate the parent class with #Inheritance to specify the inheritance strategy of your choice (SINGLE_TABLE sounds appropriate here, it will use a technical discriminator column - see http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e1168).
How about using #OneToOne and just naming the #JoinColumn differently for each type:
#Entity
public class Contact {
#Id
private String id;
private String phone;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="billingContact_ID")
private Contact billingContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="salesContact_ID")
private Contact salesContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="technicalContact_ID")
private Contact technicalContact;
public Customer() {
}
// ....
}
For each row in Customer table should create three rows in Contact table