Spring : combine 2 entities into model object - java

I have a requirement to combine 2 entity objects into 1 model object that will be used to return data back to a calling api method. Is there a common pattern or solution for this scenario?
#Entity
public class Entity1{
#Column
private String value1;
#Column
private String value2;
}
#Entity
public class Entity2{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "value1")
private Entity1 entity1;
#Column
private String value3;
}

For me you should use a Facade or a Business Delegator, that should return the object like a Adaptor of the two.

Entity2 already has Entity1, so it's sufficient to return an instance on entity2.
If you want to simplify your API, you should make a new model that combines properties of both and do the mapping behind the scenes.

Related

Localization of entity fields in Spring Hibernate

I have two entities with fields that I´d like to localize. However, I´m not sure how to implement that correctly, because I would need to have a reference to the entities as well as a reference to the field that is translated, in order to have a shared "i18n" table.
#Entity
public class EntityA {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> description;
}
Second entity
#Entity
public class EntityB {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> shortDescription;
}
Translation Entity
#Entity
#Table(name = "i18n")
public class Translation {
private String languageCode;
private String translation;
//private String referenceToEntity
//private String referenceToField
}
Is there a given way to enable internationalization on entity fields in Spring or at least some kind of workaround to make it working without too much overhead?
EDIT
I´ve written a short post about how I solved it using XmlAnyAttribute https://overflowed.dev/blog/dynamical-xml-attributes-with-jaxb/
I did some research and found this #Convert JPA annotation. You would need to encapsulate the name and description properties into an object (that implements AttributeConverter), and use a convertion class to specify how it will be translated when persisted, and how will it be translated when retreived.
To execute translations on persistence and retrieval, you can consume Google translate's API.
Here:
#Entity
public class EntityA {
#Convert(converter = DescriptionConverter.class)
private Description description
// getters and setters
},
The encapsulated object, something like:
public class Description {
private String name;
private String language;
private String description;
// Getters and Setters.
}
And the translation applies here:
#Converter
public class DescriptionConverter implements AttributeConverter<Description, String> {
#Override
public String convertToDatabaseColumn(Description description) {
// consume Google API to persist.
}
#Override
public Document convertToEntityAttribute(String description) {
// consume Google API to retrieve.
}
}
this tutorial helped me a lot. i hope it will help you too. i used the second way and it's work perfectly.Localized Data – How to Map It With Hibernate

Saving entity with one-to-many relationship

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.

Hibernate - Many to One relationship, possibility to delete last shared reference automatically?

I have a many-to-one relationship and I like that the last shared reference should get deleted by hibernate automatically. The questions are
is this is supported by hibernate?
if not can I achieve this by adding some kind of api callbacks from JPA/Hibernate rather then fully code it in the DAOs by myself?
Example I have an "Attribute" (Name/Value Pair) which is an entity and its shares some "Translation" for its name with other Attributes. So if an attribute get deleted hibernate should check if still another attribute exists where the same translation is used. If there is no one left the translation should be deleted as well.
#Entity
public class Attribute {
#Id
#GeneratedValue
private int id;
private String name;
#Lob
private String value;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
#JoinColumn(name="name_translation_id")
private Translation nameTranslation;
...
}
#Entity
public class Translation {
#Id
#GeneratedValue
private int id;
#ElementCollection (fetch=FetchType.LAZY)
#CollectionTable(name= "translation_values", joinColumns = #JoinColumn(name = "translation_id"))
#MapKeyColumn(name="language_code")
#Column(name = "value")
#Lob
private Map<String, String> values = new HashMap<String, String>();
...
}
I am using hibernate v4.3.
I think Jpa Entity Listeners good choice for you
for your question write one metod and anotate #PostRemove in Attribute.class
#PostRemove
public void removeTranslationByAttribute(Attribute attribute){
List<Attribute> attributes = AttributeRepository.getByNameTranslationId(attribute.getNameTranslation()); //get all atribute by `name_translation_id`
if(attributes.size() == 0) // when not include atrribute in list`name_translation_id`
TranslationRepository.deleteById(attribute.getNameTranslation()); // delete translation object by `name_translation_id`
}

REST API - nested objects

I'm about to design my rest API. I wonder how can I handle objects such as one below:
#Entity
public class Foo {
#ManyToMany
private Set<Bar> barSet;
#OneToMany
private Set<Zzz> zzzSet;
}
As you see object I want to expose to my rest API consists of other entity collections. I'm using Spring 4 and Jackson. Is it possible to return objects like one above - or do I have to create classes with primitive values only?
Yes, it is possible but you have to handle 2 problems :
1) at serialization, Jackson will call the getter Foo.getBarSet(). This will crash because by default, Hibernate returns lazy collections for #OneToMany and #ManyToMany relationships.
If you don't need them, annotate them with #JsonIgnore :
#Entity
public class Foo {
#JsonIgnore
#ManyToMany
private Set<Bar> barSet;
#JsonIgnore
#OneToMany
private Set<Zzz> zzzSet;
}
If you need them, you must tell hibernate to load them. For example, you can annotate #ManyToMany and #OneToMany with fetch = FetchType.EAGER (It is not the only solution btw) :
#Entity
public class Foo {
#ManyToMany(fetch = FetchType.EAGER)
private Set<Bar> barSet;
#OneToMany(fetch = FetchType.EAGER)
private Set<Zzz> zzzSet;
}
2) It can also cause some infinite loops :
Serialization of Foo calls Foo.getBarSet()
Serialization of Bar calls Bar.getFoo()
Serialization of Foo calls Foo.getBarSet()
[...]
This can be handled with #JsonManagedReference and #JsonBackReference :
#Entity
public class Foo {
#JsonManagedReference
#OneToMany
private Set<Zzz> zzzSet;
And on the other side :
#Entity
public class Zzz {
#JsonBackReference
private Foo parent;
}

JPA: Foreign key that is also a primary key mapping

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.

Categories