I have a critical issue. I have a table
TICKETINFO
TICKETINFOID pk, REMARK varchar(128), TICKETDATE timestamp
it has a corresponding class with hibernate annotation which somewhat looks like this
#Entity
#Table(name = "TICKETINFO")
public class Ticketinfo implements Serializable {
#Id
#Column(name = "TICKETINFOID")
private Long id;
#Column(name = "TICKETDATE")
private String date;
#column(name = "REMARK")
private string remark;
//getters and setters
}
now my work is that i need to create a child table of TICKETINFO table
TICKETINFO_REMARK
TICKETINFO_REMARK_ID pk, TICKETINFOID fk, REMARK varchar(128)
and TICKETINFOID will be foreign key from TICKETINFO table and have to populate the REMARK field of TICKETINFO_REMARK along with the REMARK field of TICKETINFO for the corresponding TICKETINFOID.
For 1 TICKETINFOID there will be one REMARK and it could be null.
The datatype of REMARK in Ticketinfo.java have to keep it as string.I can add extra logic but cannot change the existing flow.
Please help me as I am in a terrible mess....
It sounds like a One-To-One would do the trick for the linking up of the child table. For the remark itself, you'll probably want to hijack the accessors and mutators as I don't think Hibernate handles what you want out of the box. If you have something as an attribute of another table, faking it this way isn't orthogonal or best practice. If you can't change it, leave it as it is unless you have a VERY good reason for writing weird fudges into your code.
You can make REMARK field as #Transient and use #PreUpdate #PrePersist and #PostLoad methods to load and save remark field from/to one-to-one mapped TICKETINFO_REMARK entity. The same could be done on TICKETINFO_REMARK side.
Here is quick example, it is not tested, just to give you an idea.
#Entity
#Table(name = "TICKETINFO")
public class Ticketinfo implements Serializable {
#Id
#Column(name = "TICKETINFOID")
private Long id;
#Column(name = "TICKETDATE")
private String date;
#Transient
private string remark;
#OneToOne
#PrimaryKeyJoinColumn
private TicketinfoRemark ticketInfoRemark;
#PostLoad
public void postLoad() {
if (ticketInfoRemark != null)
this.remark = ticketInfoRemark.getRemark();
}
#PreUpdate
#PrePersist
public void prePersist() {
if (ticketInfoRemark != null)
ticketInfoRemark.setRemark(this.remark);
}
//getters and setters
}
Hope it helps.
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 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`
}
I have a (abbreviated) class that looks like this:
#Entity
#Table
#SecondaryTable(
name = "SUPER_ADMIN",
pkJoinColumns = #PrimaryKeyJoinColumn(
name = "PERSON_ID",
referencedColumnName = "PERSON_ID"))
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
// getters/setters omitted for brevity
}
The SUPER_ADMIN table has only one column: PERSON_ID. What I would like to do is add private Boolean superAdmin to Person where it would be true if the PERSON_ID is present in that table.
Is this even possible? I am using Hibernate as my JPA provider, so I'm open to proprietary solutions as well.
UPDATE
It seems like I should have done more homework. After poking around, I see that #SecondaryTable does inner joins and not outer joins. Therefore, my idea here will not work at all. Thanks to #Elbek for the answer -- it led me to this revelation.
You can use JPA callback methods.
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
#Transient
private transient Boolean superAdmin = false;
// This method will be called automatically when object is loaded
#PostLoad
void onPostLoad() {
// BTW, personId has to be present in the table since it is id column. Do you want to check if it is 1?
superAdmin = personId == 1;
}
}
or you can create easy getter method.
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
boolean isSuperAdmin() {
return personId == 1;
}
}
You can't have an optional relationship with a #SecondaryTable. You do not have any other choice than using a #OneToOne optional relationship in that case.
I have a LocalizedString Embeddable that looks like this:
#Embeddable
public class LocalizedString {
#ElementCollection(fetch = FetchType.EAGER)
private Map<String, String> stringMap;
// getter, setter
}
and an Article class that is supposed to make use of the LocalizedString:
#Entity
public class Article {
#Id
#GeneratedValue
private long id;
#Embedded
private LocalizedString title;
#Embedded
private LocalizedString text;
// getter, setter
}
Generating the tables works just fine, but when I try to insert an Article I get the following exception:
Duplicate entry '1-test2' for key 'PRIMARY'
After looking at the database structure it's obvious why. Hibernate only generated one article_string_map table with the a primary key constraint over the article id and the key of the map.
Googling this problem led me to this question on SO and the answer to include the #AttributeOverride annotations:
#Entity
public class Article {
#Id
#GeneratedValue
private long id;
#AttributeOverride(name="stringMap",column=#Column(name="title_stringMap"))
#Embedded
private LocalizedString title;
#AttributeOverride(name="stringMap",column=#Column(name="text_stringMap"))
#Embedded
private LocalizedString text;
}
This does not work either though, since Hibernate now complains about this:
Repeated column in mapping for collection:
test.model.Article.title.stringMap column: title_string_map
I do not understand what exactly is causing this error and I couldn't really translate the things I did find out about it to my specific problem.
My question is, what else do I need to fix to make LocalizedString work as an Embeddable? I'd also like to know why Hibernate is saying that I mapped title_string_map twice, even though I don't mention it twice in my entire project. Is there some kind of default mapping going on that I need to override?
How can I tell Hibernate to map this correctly?
(Also, I don't have a persistence.xml since I'm purely using annotations for configuration)
I figured it out on my own.
In order to map a ElementCollection I had to use #AssociationOverride combined with the joinTable attribute. The working Article class looks like this now:
#Entity
public class Article {
#Id
#GeneratedValue
private long id;
#AssociationOverride(name = "stringMap", joinTable = #JoinTable(name = "title_stringMap"))
#Embedded
private LocalizedString title;
#AssociationOverride(name = "stringMap", joinTable = #JoinTable(name = "text_stringMap"))
#Embedded
private LocalizedString text;
// getters, setters
}
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.