Querying Entities with maps in OpenJPA - java

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

Related

Is this relationship a #OneToMany instance?

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.

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 Mapping Problems: Multiple Embeddables + ElementCollections

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
}

JPA ManyToMany join table query

Assuming theses Entities
#Entity
public class EntityNote implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SeqEntityNote", sequenceName="SeqEntityNote", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SeqEntityNote")
private long id;
private Date date;
private String subject;
private String content;
#ManyToMany
private List<EntityTopic> listEntityTopic;
//setters/getters
#Entity
public class EntityTopic implements Serializable {
#Id
#SequenceGenerator(name="SeqEntityTopic", sequenceName="SeqEntityTopic", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SeqEntityTopic")
private long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In my DB, a join table named "entity_note_list_entity_topic" records the ManyToMany relation.
This works correctly so far.
But I'd like to perform a count query like 'how many EntityNotes per EntitityTopic'
Unfortunatly I'm quite lost in this situation.
How this query can be written ?
Do I need other elements in my two entities ?
(In many examples I see a reverse relation using mappedBy attribute on ManyToMany.. Do I need this ?)
It will be the easiest if you make the many to many relation bidirectional. There are no serious extra costs involved, as it uses the same db structure, and the list are lazy loaded so if the relation is not being used the lists are not populated (you can hide the second direction by making accessors private).
Simply change:
#Entity
public class EntityTopic implements Serializable {
...
#ManyToMany(mappedBy="listEntityTopic")
private List<EntityNote> notes;
}
You can issue normal count jpql queries, for example:
SELECT count(n) from EntityTopic t INNER JOIN t.notes n where t.name =:name
so you don't neet to retrieve the notes and topics if don't need to.
But I also believe that your original mapping can also be queries with:
SELECT COUNT(n) FROM EntityNote n INNER JOIN n.listEntityTopic t WHERE t.name = :name
If you have the following code:
#Entity
public class EntityNote implements Serializable {
#ManyToMany(fetch = FetchType.LAZY)
private List<EntityTopic> topics;
}
#Entity
public class EntityTopic implements Serializable {
#ManyToMany(fetch = FetchType.LAZY)
private List<EntityNote> notes;
}
Then, topic.getNotes().size() will give you the number of notes associated with a topic. When using Hibernate as the JPA provider, a SELECT COUNT(...) query is issued for this instead of loading all the associated notes. If this does not work for you out-of-the-box, mark the collections as extra lazy using the instructions in this post.

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