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.
Related
I'v been searching internet for answer, but nothing was working for me. There is a lot of topics with similar cases but specific details are different in a way that make them unusable for me.
So I have two tables: t_item and t_item_info:
item_id field from t_item_info table references id field from t_item table. I'm using mysql db and id column form t_item is auto incremented
I need to make unidirectional one-to-one mapping in a specific way. Here are my classes:
#Entity
#Table(name = "t_item")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn(name = "id", referencedColumnName = "item_id")
private ItemInfo info;
}
And other one
#Entity
#Table(name = "t_item_info")
public class ItemInfo {
#Id
private Long itemId;
private String descr;
}
So the point is that i need Item object to have a reference to ItemInfo object. NOT The other way!
Item -> ItemInfo --YES
Item <- ItemInfo --NO
The other thing is that i need parent (Item) id to become id for a child (ItemInfo)
For example I create Item object with null id and set it's info field with ItemInfo object which also have null id field. Like this:
{
"id": null,
"name": "Some name",
"info": {
"itemId": null,
"descr": "some descr"
}
}
Then when Item object persists hibernate should generate id for parent(Item) object and set it as itemId field for child(ItemInfo).
I have been trying to achieve this with different hibernate annotations and I noticed that no matter how hard I tried Hibernate always seems to try to persist child object first. I noticed it in the logs when I turned sql logging on. insert into t_item_info always goes first (and dies because of null id :D)
So the question is: Is it even possible to achieve this and if so what should I change in my code to do so
I hope that what I'm trying to ask makes sens to you given my poor explanations =)
Why people always insist the child object table in one-to-one associations should be the one with the foreign key is beyond me.
Anyway, as a workaround, since both objects share the id and the association is non-optional, you might as well declare the autogenerated key for the child object:
#Entity
#Table(name = "t_item_info")
public class ItemInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long itemId;
private String descr;
}
and then use #MapsId for the parent:
#Entity
#Table(name = "t_item")
public class Item {
#Id
private Long id;
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "id")
#MapsId
private ItemInfo info;
}
Note that this approach, will, in a sense, fool Hibernate into thinking it is the Item that should be treated as the child object. You have been warned.
While there is an accepted answer here it looks to me like #Secondary table would be a better and more convenient solution: you may have 2 tables at the database level but I do not see any reason that that fact needs be exposed to any client code. There does not seem to be a lot of benefit to that? The following gives you a simpler API.
Entity:
#Entity
#Table(name = "t_item")
#SecondaryTable("t_item_info", pkJoinColumns={
#PrimaryKeyJoinColumn(name="id", referencedColumnName="item_id")})
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Colulumn(name = "description", table= "t_item_info"")
private String description;
}
API:
{
"id": null,
"name": "Some name",
"descr": "some descr"
}
I have trouble in saving... fetch from child table is working
Here is my parent table pojo
#Entity
#Table(name = "Dei_Resources")
public class DeiResources {
private int id;
private String employeeId;
private Set<DeiResourceType> deiResourceType;
//other setters getters not included
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "deiResource", cascade = CascadeType.ALL)
public Set<DeiResourceType> getDeiResourceType() {
return deiResourceType;
}
Child Table pojo
#Entity
public class DeiResourceType implements Serializable{
private int id;
private int resourceId;
private String typeValue;
#JsonBackReference
private DeiResources deiResource;
//other setters getters not included
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
#ManyToOne(fetch=FetchType.LAZY,optional=false)
#JoinColumn(name = "resourceId", referencedColumnName="id",insertable = false, updatable = false)
public DeiResources getDeiResource() {
return deiResource;
}
I have DeiResourcesRepository in place, in my service Im trying this
DeiResources dei = new DeiResources();
DeiResourceType deii = new DeiResourceType();
Set<DeiResourceType> deiResourceType = new HashSet<DeiResourceType>();
deii.setTypeValue("Driver");
deiResourceType.add(deii);
dei.setEmployeeId("unique1");
dei.setDeiResourceType(deiResourceType);
deiResourcesRepository.save(dei);
Getting this error
[ERROR] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ORA-02291:
integrity constraint (DEI_ADMIN.DEI_RESOURCE_TYPE_R01) violated -
parent key not found
In DeiResourceType table I have added foreign key constrain with Parent table ID. How can I get rid of this error, any suggestion/help ?
First, in SQL there is no Child/Parent pattern. Thats why the imagination of a structure like a real family is wrong. In this example a parent can not be a child, in the real world everyone who is parent is a child too!
We only have two Tables who have a 1-n relation respecting the constraint that every DeiResource must have a DeiResourceType! As long as this constraint keeps it integrity, there is no exception.
This in mind you call the constructor of two entitys but you save only one (you save the DeiResource without DeiResourceType). So you save a Resource without a type. But the constraint that every DeiResource must have a DeiResourceType is broken and the database has no integrity anymore and the Exception is thrown!
You have two options:
You save the DeiResourceType first and then the DeiResource
You load a existing DeiResourceType first, wire it to the DeiResource and save the DeiResource then.
I would prefer the second method to avoid duplicates in the Table "DeiResourceType".
(By the way, theese constraints can be deferrable, a old, forgotten and mighty magic)
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 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.
Can anyone tell me whether Hibernate supports associations as the pkey of an entity? I thought that this would be supported but I am having a lot of trouble getting any kind of mapping that represents this to work. In particular, with the straight mapping below:
#Entity
public class EntityBar
{
#Id
#OneToOne(optional = false, mappedBy = "bar")
EntityFoo foo
// other stuff
}
I get an org.hibernate.MappingException: "Could not determine type for: EntityFoo, at table: ENTITY_BAR, for columns: [org.hibernate.mapping.Column(foo)]"
Diving into the code it seems the ID is always considered a Value type; i.e. "anything that is persisted by value, instead of by reference. It is essentially a Hibernate Type, together with zero or more columns." I could make my EntityFoo a value type by declaring it serializable, but I wouldn't expect this would lead to the right outcome either.
I would have thought that Hibernate would consider the type of the column to be integer (or whatever the actual type of the parent's ID is), just like it would with a normal one-to-one link, but this doesn't appear to kick in when I also declare it an ID. Am I going beyond what is possible by trying to combine #OneToOne with #Id? And if so, how could one model this relationship sensibly?
If the goal is to have a shared primary key, what about this (inspired by the sample of Java Persistence With Hibernate and tested on a pet database):
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address shippingAddress;
//...
}
This is the "parent" class that get inserted first and gets a generated id. The Address looks like this:
#Entity
public class Address implements Serializable {
#Id #GeneratedValue(generator = "myForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "myForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "user")
)
#Column(name = "ADDRESS_ID")
private Long id;
#OneToOne(mappedBy="shippingAddress")
#PrimaryKeyJoinColumn
User user;
//...
}
With the above entities, the following seems to behave as expected:
User newUser = new User();
Address shippingAddress = new Address();
newUser.setShippingAddress(shippingAddress);
shippingAddress.setUser(newUser); // Bidirectional
session.save(newUser);
When an Address is saved, the primary key value that gets inserted is the same as the primary key value of the User instance referenced by the user property.
Loading a User or an Address also just works.
Let me know if I missed something.
PS: To strictly answer the question, according to Primary Keys through OneToOne Relationships:
JPA 1.0 does not allow #Id on a OneToOne or ManyToOne, but JPA 2.0 does.
But, the JPA 1.0 compliant version of Hibernate
allows the #Id annotation to be used on a OneToOne or ManyToOne mapping*.
I couldn't get this to work with Hibernate EM 3.4 though (it worked with Hibernate EM 3.5.1, i.e. the JPA 2.0 implementation). Maybe I did something wrong.
Anyway, using a shared primary key seems to provide a valid solution.
Yes that is possible.
Look at the following example using Driver and DriverId class as id for Driver.
#Entity
public class Drivers {
private DriversId id; //The ID which is located in another class
public Drivers() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "personId", column = #Column(name = "person_id", nullable = false))})
#NotNull
public DriversId getId() {
return this.id;
}
//rest of class
}
Here we are using personId as the id for Driver
And the DriversId class:
//composite-id class must implement Serializable
#Embeddable
public class DriversId implements java.io.Serializable {
private static final long serialVersionUID = 462977040679573718L;
private int personId;
public DriversId() {
}
public DriversId(int personId) {
this.personId = personId;
}
#Column(name = "person_id", nullable = false)
public int getPersonId() {
return this.personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof DriversId))
return false;
DriversId castOther = (DriversId) other;
return (this.getPersonId() == castOther.getPersonId());
}
public int hashCode() {
int result = 17;
result = 37 * result + this.getPersonId();
return result;
}
}
You can do this by sharing a primary key between EntityFoo and EntityBar:
#Entity
public class EntityBar
{
#Id #OneToOne
#JoinColumn(name = "foo_id")
EntityFoo foo;
// other stuff
}
#Entity
public class EntityFoo
{
#Id #GeneratedValue
Integer id;
// other stuff
}
You have to use #EmbeddedId instead of #Id here.
And EntityFoo should be Embeddable.
Another way is to put an integer, and a OneToOne with updateble and instertable set to false.