I'm using java persistence to save a list of entities that are associated to another entity. Here's a quick rundown of where I'm having some problems.
#Entity public class Offer implements Serializable {
#Id private Long offerId;
#OneToMany
#Column List<OfferCategory> offerCategories;
}
#Entity public class OfferCategory implements Serializable {
#Embeddable public static class Id implements Serializable
{
#Column(name="offer_id")
private Long offerId;
#Column(name="category_id")
private Long categoryId;
public Id() {}
public Id(Long offerId, Long categoryId) {
this.offerId = offerId;
this.categoryId = categoryId;
}
public boolean equals(Object o) {
if(o != null && o instanceof Id) {
Id other = (Id) o;
return this.offerId.equals(other.offerId) &&
this.categoryId.equals(other.categoryId);
}
else
return false;
}
public int hashCode() {
return offerId.hashCode() + categoryId.hashCode();
}
}
#EmbeddedId private Id id = new Id();
}
Essentially, due to an architecture I cannot change, I need to save a list of Categories as being assigned to an Offer.
Right now, I'm getting a list of Categories from the user and then putting them into the offerCategories field of Offer. However, this doesn't work for new Offers, because there's no way for me to set the ID of a new item.
I'm new to JPA and Seam, so if someone could give me a nudge in the right direction it would be greatly appreciated.
I have not tried using a composite ID before, but one thing to note is that #Column is only used to change the properties of the database column the field is using. It doesn't stipulate a relation, so you still need something like this:
#OneToMany
List<OfferCategory> offerCategories;
As I looked into tutorial I found this:
You cannot use an IdentifierGenerator to generate composite keys. Instead the application must assign its own identifiers.
So you have to assign the id by yourself. Maybe you can make a DB sequence and fetch its values with native query?
And one remark - if you want to use List mapping (order of Categories in Offer is defined by database), you need an index column to contain the index in list. If the order of categories is not important, Set would be more convenient.
Well, my solution for now is that I persist each one (creating keys for new entries), then stuff them into a list, then stuff them into their container object, then persist that object.
Related
I am using JPA and have a view I would like to access. I am using a mapped entity with an embedded Id in many of my other classes to access tables with similar requirements. However here whenever there are nulls in the view that comprise the id, the whole object is returned as null. There are the right number of entities returned when i query, but they are null.
Here are the classes:
{
#Entity
#Table(name = "VW_PRODUCT")
public class VwProduct implements Serializable {
#EmbeddedId
private VwProductId id;
public VwProduct() {
}
}
{
#Embeddable
public class VwProductId implements java.io.Serializable {
#Column(name = "PROD_NAME", nullable=true)
private String prodName;
#Column(name = "PROD_CTGRY", nullable=true)
private String prodCtgry;
#Column(name = "PROD_SBCTGRY", nullable=true)
private String prodSbctgry;
}
I omitted things like getters and setters and hashcode but i think my question is clear; how do I access this view, when some of its values are null?
Thank you!
If you have a primary key column with null, and search on that column will always return no objects.
There are three possible solutions that I am aware of, in order of complexity/wonkiness. (For people not working on a read-only view, do not do any of the following. You will blow your foot off with an incredibly large shotgun.)
The easiest answer is to change your definition of the view and add something like a rowid or generated serial and then make that the primary key.
The second answer, and this is both implementation specific and hibernate specific, is to have a primary key of #ID #ROWID String id;
The last answer, is more complex, is by mapping all three of your fields to a "NullableString" UserType, and have a nullSafeGet that maps null to something non-null, like '' or something. You'll get duplicates, but since this is a read-only view, you don't really care.
I use hibernate sequences to generate id of an entity. I use PostgreSQL 9.1.
Is it possible to get entity id before it is saved to database? How?
You explicitely create a separate sequence, get its value, then insert an object with id based on that value. You will have more code, but the ID will be available before the insertion and the guarantees for sequences are exactly the same as for serially given IDs, because they are essentially the same.
In other words:
create your own sequence
make a primary key a simple int not serial
get a number from sequence
use it as an ID for your object
This question has an answer saying how to get next sequence value.
save() method returns the id of the entity that is saved. You can use it!
reference:-> http://docs.jboss.org/hibernate/annotations/3.5/api/org/hibernate/Session.html
You can implement the interface org.hibernate.id.IdentifierGenerator and create a Id generator.
Example:
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;
public class TimeBasedIDGenerator implements IdentifierGenerator {
private static TimeBasedGenerator generator = Generators.timeBasedGenerator();
private static TimeBasedIDGenerator SINGLETON = new TimeBasedIDGenerator();
public static UUID generate() {
return SINGLETON.generateUUID();
}
#Override
public Serializable generate(SessionImplementor session, Object parent) throws HibernateException {
return generator.generate();;
}
}
This can be used in your Entities like this. So the id is generated by the constructor:
#Entity
public EntityClassName {
private UUID uuid;
private Integer mandatoryField;
public EntityClassName() {
}
public EntityClassName(Integer mandatoryField) {
this.uuid = TimeBasedIDGenerator.generate();
this.mandatoryField = mandatoryField;
}
#Id
#Column(name = COLUMN_XXX_UUID)
#Type(type = "java.util.UUID")
public UUID getUuid() {
return uuid;
}
// setter + other properties
}
I have the below unidirectional Many To One mapping
#Entity
public class Item implements Serializable {
private Integer id;
private Double amount;
private Country origin;
#ManyToOne(optional=true)
#JoinColumn
public Country getOrigin() {
return this.origin;
}
}
#Entity
public class Country implements Serializable{
private String code;
private String desc;
}
Let say the relationship is optional so I am trying to remove the relation by updating it to null using code below
Country country = null;
//item is detached
item.setOrigin(country);
em.merge(item);
But the result turns out to be relationship is not removed.
However, this code works fine if country is not null and the system can update the relationship in DB.
It just simply ignore the field if it's null.
Can someone points out what setting can be changed in order to achieve my desired result?
P.S. Please be reminded that I am not wanting to delete the entity Country, but just remove the relationship between them.
Thanks all it's a mistaken question. It actually works.
There's just some client side issue submitting wrong data to it.
Lets say we have User entity class. User can be friends with other users. How can i map this self-reference collection field without creating a new entity called Connection or creating multiple entries in the database?
#Entity
public class User {
...
#ManyToMany
private Collection<User> friends;
...
}
USER_ID-FRIEND_ID
1 - 2
2 - 1 (duplicate... I don't need it)
Following is snapshot from my code for ElementEntity:
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<ElementEntity> children;
#JoinColumn(name = "ParentId", referencedColumnName = "ElementId")
#ManyToOne(fetch = FetchType.LAZY)
private ElementEntity parent;
Where on database there are fields:
ElementId - primary key;
ParentId relation with parent
You can't - you need both records in the database.
Actually, for friendship relations, I'd say that a graph database like neo4j is the proper thing to use. There you have the two users and simply add an edge "friends".
At least you will need a relational table.
So you have a USER table and a FRIENDS:
user_id friend_id
1 2
But #Bozho answer is way better than mine (neo4j).
Well, in fact you can.
You can use annotations like #PreUpdate, #PrePersists, #PostUpdate and so to convert manually the elements of a collection. This way your entity can render then them way you want while in database you only store a raw text.
A more pausible alternative will be to use #Convert annotation, available since jpa 2.1 (#UserType in hibernate). It tells jpa to convert the field into another type everytime it read/save in database.
For it you should use #Convert anotation, specifying and AttributeConverter object.
For example
public class Parent {
#Id
private Integer id;
#Convert(converter = FriendConverter.class)
private Set<Parent>friends;
}
And converter class like the following:
#Component
public class FriendConverter implements AttributeConverter<List, String>{
#Autowired
private SomeRepository someRepository;
#Override
public String convertToDatabaseColumn(List attribute) {
StringBuilder sb = new StringBuilder();
for (Object object : attribute) {
Parent parent = (parent) object;
sb.append(parent.getId()).append(".");
}
return sb.toString();
}
#Override
public List convertToEntityAttribute(String dbData) {
String[] split = dbData.split(".");
List<Parent>friends = new ArrayList<>();
for (String string : split) {
Parent parent = someRepository.findById(Integer.valueOf(string));
friends.add(accion);
}
return friends;
}
}
It is a dummy implementation but it gives you the idea.
As a personal comment, I do recommend to map the relationship as it should. In the future it will avoid you problems. AttributeConverter comes in handy when working with enums
I need to read a complex model in an ordered way with eclipselink. The order is mandantory because it is a huge database and I want to have an output of a small portion of the database in a jface tableview. Trying to reorder it in the loading/quering thread takes too long and ordering it in the LabelProvider blocks the UI thread too much time, so I thought if Eclipselink could be used that way, that the database will order it, it might give me the performance I need. Unfortunately the object model can not be changed :-(
The model is something like:
#SuppressWarnings("serial")
#Entity
public class Thing implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
#PrivateOwned
private List<Property> properties = new ArrayList<Property>();
...
// getter and setter following here
}
public class Property implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
#OneToOne
private Item item;
private String value;
...
// getter and setter following here
}
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
....
// getter and setter following here
}
// Code end
In the table view the y-axis is more or less created with the query
Query q = em.createQuery("SELECT m FROM Thing m ORDER BY m.name ASC");
using the "name" attribute from the Thing objects as label.
In the table view the x-axis is more or less created with the query
Query q = em.createQuery("SELECT m FROM Item m ORDER BY m.name ASC");
using the "name" attribute from the Item objects as label.
Each cell has the value
Things.getProperties().get[x].getValue()
Unfortunately the list "properties" is not ordered, so the combination of cell value and x-axis column number (x) is not necessarily correct. Therefore I need to order the list "properties" in the same way as I ordered the labeling of the x-axis.
And exactly this is the thing I dont know how it is done. So querying for the Thing objects should return the list "properties" "ORDER BY name ASC" but of the "Item"s objects. My ideas are something like having a query with two JOINs. Joing Things with Property and with Item but somehow I was unable to get it to work yet.
Thank you for your help and your ideas to solve this riddle.
May be the answer to this other question could help you:
Defining the order of a list
I think you may have to use another query to get the list of properties order by item.name for each thing.
Something like:
SELECT p FROM Property p WHERE p.thing = ?1 ORDER BY p.item.name
Try the #OrderBy JPA annotation. Something like
#OrderBy('name ASC')
#OneToMany(cascade=CascadeType.ALL)
private List<Property> properties = new ArrayList<Property>();