I already have two tables:
zip_codes_germany
and
travel_agencies
filled with some data. Where travel_agencies has a composite key from columns name and city and zip_codes_germany has the primary key zip_code.
Let the condition be fulfilled that the city names are the same in both tables. Now one city maps to multiple zip_codes.
What is the best way to make this relationship clear in order to receive the correct list of zip codes when loading a TravelAgency object using hibernate?
I'm thinking of something like this:
#Entity
#Table(name = "travel_agencies")
public class TravelAgency implements Serializable {
private static final long serialVersionUID = -5215122407119218666L;
#Id
#Column(name = "name")
private String name;
#Id
#Column(name = "city")
private String city;
#Column(name = "ceo")
private String ceo;
#OneToMany
#JoinColumn(name = "city")
private Set<ZipCodeArea> zipCodeAreas;
I'm new to hibernate and working with databases in general so I'm not quite sure if it is the correct tool to get what I'm asking for or if I'd better go with something else?
As commented by OH GOD SPIDERS, I do have a many to many relationship here.
Related
I am trying to read data from another table based on the value of one of the fields in the current entity. But somehow I am facing an issue selecting multiple fields inside the formula.
#Entity
#Table(name = "contacts")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class ContactInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "url")
private String imageUrl;
//Not working
#Formula("(select code,area from areas where area_id=id)")
private Map<String, String> vals;
//working
#Formula("(select code from areas where area_id=id)")
private String someVal;
}
is there any way that I can use the formula for retrieving multiple columns of data with multiple rows?
Thanks for your help.
I'm not sure you can retrieve like Map<string, String>. Suppose you declare another field how you going to retrieve. you can receive as a List instead of Map<st..., str..>.
Instead of #Formulae, you can use #OneToMany & #ManyToOne. through a Bi-direction connection, you achieve your scenario.
This looks more like it should be using JPA's ElementCollection mapping to the Area table, with a MapKeyColumn mapping:
public class ContactInfo {
..
#ElementCollection
#MapKeyColumn(name="code")//key
#Column(name="area") //value
#CollectionTable(name="areas",
joinColumns=#JoinColumn(name="area_id"))//fk from Areas->contacts.id
private Map<String, String> vals;
..
See this for some more information and examples.
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'm trying to write a hibernate adapter for an old database schema. This schema does not have a dedicated id column, but uses about three other columns to join data.
On some tables, I need to use coalesce. This is what I came up with so far:
About the definition:
A car can have elements, assigned by the car's user or by the car's group of users.
If FORIGN_ELEMENT holds a user's name, definition will be 'u'
If FORIGN_ELEMENT holds a group's name, definition will be 'g'
This also means, one table (CAR_TO_ELEMENT) is misused to map cars to elements and cargroups to elements. I defined a superclass CarElement and subclasses CarUserElement and CarGroupElement.
state is either "active" or an uninteresting string
I set definitition and state elsewhere, we do not need to worry about this.
Use DEP_NR on the join table. If it's zero, use USR_DEP_NR. I did this with COALESCE(NULLIF()) successfully in native SQL and want to achieve the same in Hibernate with Pojos.
Okay, here we go with the code:
#Entity
#Table(name="CAR")
public class Car extends TableEntry implements Serializable {
#Id
#Column(name="DEP_NR")
private int depnr;
#Id
#Column(name="USER_NAME")
#Type(type="TrimmedString")
private String username;
#ManyToOne(fetch = FetchType.EAGER, targetEntity=CarGroup.class)
#JoinColumns(value={
#JoinColumn(name="GROUP_NAME"),
#JoinColumn(name="DEP_NR"),
#JoinColumn(name="state"),
})
private CarGroup group;
#OneToMany(fetch=FetchType.EAGER, targetEntity=CarUserElement.class, mappedBy="car")
private Set<CarUserElement> elements;
}
#Entity
#Table(name="CAR_GROUP")
public class CarGroup extends TableEntry implements Serializable {
#Id
#Column(name="DEP_NR")
private int depnr;
#Id
#Column(name="GROUP_NAME")
#Type(type="TrimmedString")
private String group;
#ManyToOne(fetch = FetchType.EAGER, targetEntity=Car.class)
#JoinColumns(value={
#JoinColumn(name="GROUP_NAME"),
#JoinColumn(name="DEP_NR"),
#JoinColumn(name="state"),
})
private Set<Car> cars;
#OneToMany(fetch=FetchType.EAGER, targetEntity=CarGroupElement.class, mappedBy="car")
private Set<CarGroupElement> elements;
}
#MappedSuperclass
public class CarElement extends TableEntry {
#Id
#ManyToOne(fetch = FetchType.EAGER, targetEntity=Element.class)
#JoinColumns(value={
#JoinColumn(name="ELEMENT_NAME"),
#JoinColumn(name="state"),
})
private Element element;
}
#Entity
#Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
#Id
#Column(name="DEFINITION")
private char definition;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula=#JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
#JoinColumnOrFormula(column=#JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="USER_NAME")),
#JoinColumnOrFormula(column=#JoinColumn(name="STATE", referencedColumnName="STATE"))
})
private Car car;
}
#Entity
#Table(name="CAR_TO_ELEMENT")
public class CarGroupElement extends CarElement {
#Id
#Column(name="DEFINITION")
private char definition;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula=#JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
#JoinColumnOrFormula(column=#JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="GROUP_NAME")),
#JoinColumnOrFormula(column=#JoinColumn(name="STATE", referencedColumnName="STATE"))
})
private Car car;
}
I tried all available versions of hibernate (from 3.5.1 [first version with #JoinColumnsOrFormulas] up to 4.x.x), but I always get this error:
Exception in thread "main" java.lang.ClassCastException: org.hibernate.mapping.Formula cannot be cast to org.hibernate.mapping.Column
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:351)
at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1338)
at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:791)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:719)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:668)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:66)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1597)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1355)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1788)
Other hibernate users seem to have the same problem: They can't get it working with any version, see this thread and other stackoverflow questions:
https://forum.hibernate.org/viewtopic.php?f=1&t=1010559
To be more complete, here's my TrimmedString Class:
https://forum.hibernate.org/viewtopic.php?p=2191674&sid=049b85950db50a8bd145f9dac49a5f6e#p2191674
Thanks in advance!
PS: It works with joining just these three colulmns with just one DEP-NR-Column (i.e. either DEP_NR OR USR_DEP_NR using just #JoinColumns). But I need this coalesce(nullif()).
I ran into a similar problem, and it seems that the issue is that you are using a #Formula inside an #Id. Hibernate wants Ids to be insertable, and Formulas are read-only.
In my case I was able to work around the problem by making the individual columns Id properties on their own, and making the joined object a separate property. I don't know if this would work in your case since you're using two different columns in your formula, but if so your code might look something like:
#Entity
#Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
#Id
#Column(name="DEFINITION")
private char definition;
#Id
#Column(name="DEP_NR")
private Integer depNr;
#Id
#Column(name="USR_DEP_NR")
private Integer usrDepNr;
#Id
#Column(name="FORIGN_ELEMENT")
private String userName;
#Id
#Column(name="STATE")
private String state;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula=#JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
#JoinColumnOrFormula(column=#JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="USER_NAME", insertable = false, updatable = false)),
#JoinColumnOrFormula(column=#JoinColumn(name="STATE", referencedColumnName="STATE", insertable = false, updatable = false))
})
private Car car;
}
Join formulas are very fragile in Hibernate for the time being; I always had a difficult time to get them work properly.
The workaround that helped me often was to create database views which exposed the proper columns (including foreign keys that don't exist in the original tables). Then I mapped the entities to the views using classing Hibernate/JPA mappings.
Sometimes there are redundant joins in the generated SQL when using such entities, but the database optimizes such queries in most cases so that the execution plan is optimal anyway.
Another approach could be using #Subselects, which are some kind of Hibernate views, but I expect them to be less performant than the classic database views.
I ran into the cast exception as well and I'm on Hibernate 5.x.
Until Hibernate dedicates time to fix the issue, I found that while this guy's approach may not be cleanest (he even eludes to that fact!), it works.
You just need to add the #Column mappings (and get/set methods) to your association table objects that are returning null and manually set the values when you populate the relation data. Simple but effective!
I have the following Entities
#Entity
public class Conversation implements Serializable {
#Id
private int Id;
#Column
private Alias AliasA;
// SNIP
}
and
#Entity
public class Alias implements Serializable {
#Id
private String alias;
#Column
private String personalName;
#OneToMany(mappedBy = "alias", cascade = CascadeType.ALL)
#MapKeyColumn(name="address")
private Map<String, Recipient> recipients = new HashMap<String, Recipient>();
}
and
#Entity
public class Recipient implements Serializable {
#Id
#GeneratedValue
private long id;
#Column
private String address;
#Column
private RecipientStatus status;
#ManyToOne
private Alias alias;
}
And I would like to make something like the following JPQL query
SELECT conversation FROM Conversation conversation WHERE :sender MEMBER OF conversation.aliasA.recipients AND conversation.adId=:adID
Where :sender is in the key of my Map. The MEMBER OF keyword however only seems to work with Sets and not with Maps. I believe that JPA 2.0 should offer the KEY keyword, but this doesn't seem to be implemented in OpenJPA yet. Is there an alternative to this?
Update: Added information to clarify my question.
There is a VALUE keyword that should allow you to something like this:
SELECT c FROM Conversation c JOIN c.aliasA a JOIN a.recepients r
WHERE VALUE(r) = :sender AND conversation.adId=:adID
While axtavt's answer gave me the hint I needed, the answer was actually, that the error checking in IntelliJ 10.5.4 is not to be trusted in this case.
The KEY keyword does indeed work and the correct query was
SELECT conversation FROM Conversation conversation JOIN conversation.aliasA.recipients recipients WHERE KEY(recipients) = :senderAddress AND conversation.adId=:adID
So the situation is as follows: there is an entity which needs to be connected with a dictionary. Imagine a following structure
create table Address (
addressId bigint not null,
addressLine1 varchar(255),
city varchar(255),
country varchar(255),
state varchar(255),
zipCode varchar(255),
primary key (addressId)
)
create table STATES_DICT (
state_code varchar(255),
state_fullname varchar(255),
primary key (state_code)
)
I want to map both ADDRESS and STATE_DICTIONARY into a single entity.
#Entity
#Table(name = "ADDRESS")
public class Address implements Serializable {
#Id
#Column(name = "ADDRESSID")
private int addressId;
#Column(name = "ADDRESSLINE1")
private String addressLine1;
#Column(name = "STATE")
private String state;
//??? annotations
private String fullStateName;
#Column(name = "ZIPCODE")
private String zipCode;
#Column(name = "CITY")
private String city;
#Column(name = "COUNTRY")
private String country;
//... getters and setters
}
For a pure SQL I'll run
select a.ADDRESSID, a.ADDRESSLINE1, a.CITY, a.ZIPCODE, a.STATE,
d.STATE_FULLNAME, a.COUNTRY
from ADDRESS a, STATES_DICT d where a.STATE = d.STATE_CODE
but I'm having severe problems with mapping it with JPA.
I cannot use #SecondaryTable because the tables are not mapped by primary keys
The best I could get was:
#ElementCollection
#JoinTable(name="STATES_DICT",
joinColumns=#JoinColumn(name="STATE_CODE", referencedColumnName="STATE"))
#Column(name = "STATE_FULLNAME")
private Collection<String> fullStateName;
Downside is - the mapping is always one-to-one and the Collection brings confusion and the relation is more of one-to-one (many-to-one) not one-to-many.
Any ideas? Is there an equivalent of #ElementCollection for one-to-one mappings?
Dropping the #ElementCollection does not help. fullStateName field is expected to be in ADDRESS column - which is not the case.
Some notes:
* I need those two to keep together in a single entity.
* I'm extending existing solution, need to add just this dictionary column
* The entity is processed later on by some other service which runs through primitive types only. I'd rather not change the service, that's why adding a #OneToOne relation is not preferable
Many thanks
I'm extending the question with #SecondaryTable example - which didn't work for me.
#Entity
#Table(name = "ADDRESS")
#SecondaryTable(name="STATES_DICT",
pkJoinColumns=#PrimaryKeyJoinColumn(columnDefinition="STATE_CODE", referencedColumnName="STATE"))
public class Address implements Serializable {
#Id
#Column(name = "ADDRESSID")
private int addressId;
#Column(name = "ADDRESSLINE1")
private String addressLine1;
#Column(name = "STATE")
private String state;
#Column(table="STATES_DICT", name = "STATE_FULLNAME")
private String fullStateName;
#Column(name = "ZIPCODE")
private String zipCode;
#Column(name = "CITY")
private String city;
#Column(name = "COUNTRY")
private String country;
//... getters and setters
}
That caused a nasty exception of:
Caused by: org.hibernate.AnnotationException: SecondaryTable JoinColumn cannot reference a non primary key
For the record - I couldn't find a way to do this (and assumed it's simply not the way it should be done). I've gone with relation annotations (#ManyToOne, #OneToOne) and #JoinColumn - so the proper way. I've adjusted the further processing logic to treat #JoinColumn annotations in the same way it's working with #Column. It worked.
The further processing is a security feature which suppresses values based on user roles and original database column names. That's why it was so important for me to stick with the #Column annotation
Answer from user2601805 is correct using
#PrimaryKeyJoinColumn(name="STATE_CODE", referencedColumnName="STATE")
In your case, this should suffice as all you want is a property from STATES_DICT.
I also asked a question relating to JPA that shows an example for using #SecondaryTable and #Embeddable to achieve something similar to #ElementCollection but for #OneToOne
Also see this blog for example http://www.bagdemir.com/2013/03/03/mapping-embeddable-objects-whichve-no-identities-using-multiple-tables-with-jpahibernate/
You should be able to create an #Embeddable State class that maps the relationship, but provide a delegate method to expose the state name as a property. Would this accomplish what you're trying to do?
In #pkJoinColumns you should use:
#PrimaryKeyJoinColumn(name="STATE_CODE", referencedColumnName="STATE")
I have same problem and your hint to use #SecondaryTable solved my problem. See Mapping one entity to several tables for more information.