transient domain instance jpa&spring - java

My domain objects are roughly like:
#Entity
public class Customer{
#Id
private String id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
private List<Account> accountList;
}
#Entity
public class Account{
#Id
private String id;
private String name;
#OneToMany
private List<Service> serviceList;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(nullable=false)
private Customer customer;
}
#Entity
public class Service{
#Id
private String id;
private String name;
#ManyToOne
#JoinColumn(nullable=false)
private Account account;
}
I have a transactional Spring service. I want to return an Account instance to fronthand but I don't want to send Customer and Service List informations because of bandwith issues.
When I do:
account.setServiceList(null);
It gives no error but after a while it deletes all my service list.
When I do:
account.setCustomer(null);
It says customer_id of account cannot be null.
I just want to return a transient instance without a validation. How can I handle this.

The solution to your problem is to make the entity detached, by calling entityManager.detach(accountInstance) (or entityManager.clear() if you use JPA 1.0 to detach all entities) and only THAN change anything to it. If you use Hibernate's sessions, use the Session.evict() method.
The other solution is to use a DTO, something that has only the fields that you need.
PS: I think you have an bug, in which the bidirectional relationships are missing on one side the mapped property. E.g in Customer you should have something like
#OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
private List<Account> accountList;
The same with the other relationship.

Related

Add a field to JSON [duplicate]

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.

JPA: How to map only one property from another entity

Let's say I have two entities:
#Entity
public class Phone {
#Id
private Long id;
private String number;
}
#Entity
public class Person {
#Id
private Long id;
private String name;
}
The relationship between a person and a phone is one to one.
How could I access only the phone's number in the Person entity mapped by the phone's id
#Entity
public class Person {
#Id
private Long id;
private String name;
// ???
private String phoneNumber;
}
The reason for not mapping the whole entity is because in some more realistic entities there are too many properties.
I don't think you can, but something like this might be acceptable:
public class Person {
#OneToOne
#JoinColumn(name = "phone_id")
private Phone phone;
public String getPhoneNumber() {
return phone.getNumber();
}
}
Although you have mapped the whole object, not just the single property, you have only exposed the single property you want. The other stuff is hidden.
Alternatively, do it at the DB layer using a View:
create view person_with_phone as
select p.id, p.name,f.number
from person p
join phone f on f.id=p.phone_id
and then have an entity class to match the view. You'll need to turn off schema creation in your JPA implementation.

Hibernate: Repeated Column in Mapping

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.

HQL strategy to equal embedded entities

i'm curious about how HQL would assert equality between an entity instances.
Let's say I have a Entity called Person
#Entity
public class Person{
#Id
private Long id;
private String name;
}
and Department
#Entity
public class Department {
#Id
private Long id;
#ManyToOne
private Person person;
}
then it's fine if I do the following statement:
Query query = getSession().createQuery("from Department d where d.person = ?");
query.setProperty(0,new Person(1L));
but, what if I have an Embedded entity and no pk defined? like
#Embeddable
public class Adress {
private String email;
private String street;
private Long identifier;
}
#Entity
public class Person{
#Id
private Long id;
private String name;
#Embedded
private Address address;
}
would have any way so I could tell JPA to make it work:
Query query = getSession().createQuery("from Person p where p.address = ?");
query.setProperty(0,new Address(1L));
even though it's not exactly a primary key?
For sure i know i'd work if I tried p.adress.identifier, and then passed just the Long value, but the point is, can I tell JPA provider how it's gonna kind of 'implement' equality my way?
Thank you all
No, it is not supported and it would be difficult in general or would not make sense in some situations, like when there are collections in the Embeddable.
If you find that you need this often though, consider converting such Embeddables to custom user types. Then you can perform comparisons the way you described.

hibernate different types of one-to-one relationships on a single class

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

Categories