I'm getting into problems with JPA. I'm trying to implement a database that allows users to follow other users and be followed.
I think I'd need (summing up) something like this:
USER_TABLE: id | userName
RELATIONSHIP_TABLE: id | follower | followed | acceptation
I have two entities (also summed up):
#Entity
public class User implements Serializable {
#Id
private Long id;
private String userName;
#OneToMany
private Collection<Relationship> followings;
}
#Entity
public class Relationship implements Serializable {
#Id
private Long id;
private User follower;
private User followed;
private boolean accepted;
}
My problem is that I'm not sure if it's possible to do this, because I obtain more tables that the two that I need.
Can anybody help me?
Thanks and sorry about my english.
You obtain more tables because you did not make the associations bidirectional. JPA has no way to know that Relationship.follower is the other side of the User.followings if you don't tell:
#Entity
public class User implements Serializable {
#OneToMany(mappedBy = "follower")
private Collection<Relationship> followings;
// ...
}
#Entity
public class Relationship implements Serializable {
#ManyToOne
#JoinColumn(name = "follower")
private User follower;
#ManyToOne
#JoinColumn(name = "followed")
private User followed;
// ...
}
The documentation of course explains how that works.
Related
I'm trying to isolate some core entities from my project. The idea of this is to share the same users, roles, etc. between many applications.
So, I have this User entity structure from core application, using a database called core:
#Entity
public class MyUser implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#Column(unique = true)
private String username;
private String password;
private String salt;
#ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;
private Boolean isAccountNonExpired;
private Boolean isAccountNonLocked;
private Boolean isCredentialsNonExpired;
private Boolean isEnabled;
...
}
On the other hand, I have this other entity from the final application, using a database called myapplication:
#Entity
public class Employee extends MyUser {
private static final long serialVersionUID = 1L;
#Type(type = "date")
private Date birthDate;
#Type(type = "date")
private Date marriageDate;
private Long employeeNum;
private Integer workHs;
#OneToMany
private List<Role> canAssignRoles;
}
I created two datasources and session factories in hibernate.cfg.xml, one for each database, but I cannot achieve the purpose.
How you can see, one entity is mapped to core database, and the other one is mapped to the myapplication database. The Employee entity has a OneToMany mapping to Role.
I have no idea of how to solve this. When Hibernate is creating the database entities, it raises an error when trying to create the entities for myapplication.
I'm new to App Engine and trying to figure out how to use relationships between entities.
I'm using JPA and having trouble to understand how to organize the relationships.
I have three classes City, Hotel and Attraction. I want cities to be standalone and able to be created on it's own. Every city has a list of all available hotels in the city. The hotel always need a city and can only have one city. Attractions have to have a city, but a city doesn't need to know about the attractions.
Classes:
#Entity(name = "City")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(??)
private List<Hotel> hotels;
//getters and setters
}
#Entity(name = "Hotel")
public class Hotel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String name;
#ManyToOne(??)
private City city;
}
#Entity(name = "Attraction")
public class Attraction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Unowned??
private City city;
//getters and setters
}
I've been looking all over the web, but i can't find any good tutorials on this. Would really appreciate any pointers in the right direction!
There is series of posts - "JDO/JPA Snippets That Work" on appengine java google group, which is a good starting point.
This one shows how to create a bidirectional, owned, one-to-many relationship.
I have an entity called ReferenceForm which contains an AutoPopulatingList of ReferenceItems. It looks like this:
#Entity
public class ReferenceForm implements Serializable{
private static final long serialVersionUID = -5633788166190438576L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
#lob
private AutoPopulatingList<ReferenceItem> referenceItems;
}
If I add no annotation at all to the AutoPopulatingList, the field type which hibernate creates is varbinary(255). This causes string truncation errors. To work around this, I used the #lob annotation. This felt questionable at the time, but it worked fine. At this point I was just using HSQLDB.
Now the application needs to run against MSSQL. I have generated the schema using Hibernate, and referenceItems ia an image column on the ReferenceForm table. The items themselves are stored in the ReferenceItem table.
Is #lob an appropriate annotation here?.
EDIT: ReferenceItem looks like this:
#Entity
public class ReferenceItem implements Serializable {
private static final long serialVersionUID = -9077063073733429102L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
private Title title;
private String firstName;
private String surname;
private String positionHeld;
private String institutionCompany;
#Embedded
private Address address;
#Embedded
private Telephone telephone;
private String email;
private boolean existingReference;
private String fileName;
public ReferenceItem() {
}
...getters and setters
}
SECOND EDIT:
Thanks to Willome for suggesting using #OneToMany. In the end, this is what worked.
//from
#lob
private AutoPopulatingList<ReferenceItem> referenceItems;
//to
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<ReferenceItem> referenceItems = new AutoPopulatingList<ReferenceItem>(ReferenceItem.class);
#OneToMany accurately describes the nature of the relationship
Use the interface (List) instead of the implementation when defining the field. See http://docs.jboss.org/hibernate/core/3.3/reference/en/html/collections.html
Define the CascadeType, otherwise this error appears on saving the entity: org.hibernate.TransientObjectException: object references an unsaved transient instance
Make the FetchType EAGER otherwise you cannot load the form in a different transaction: this error appears: failed to lazily initialize a collection of role: ReferenceForm.referenceItems, could not initialize proxy - no Session
You should replace your #Lob annonation with a #OneToMany and replace the AutoPopulatingList with a collection-valued field declared as an interface type (Check out the topic 6.1. Persistent collections on this link http://docs.jboss.org/hibernate/core/3.3/reference/en/html/collections.html.)
//#Lob
#OneToMany(mappedBy = "referenceForm")
private AutoPopulatingList<ReferenceItem> referenceItems; //fail AutoPopulatingList is not an interface
#OneToMany(mappedBy = "referenceForm")
private Set<ReferenceItem> referenceItems; // OK with Set/Collection/List
Thanks to Willome for suggesting using #OneToMany. In the end, this is what worked.
//from
#lob
private AutoPopulatingList<ReferenceItem> referenceItems;
//to
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<ReferenceItem> referenceItems = new AutoPopulatingList<ReferenceItem>(ReferenceItem.class);
#OneToMany accurately describes the nature of the relationship
Use the interface (List) instead of the implementation when defining
the field. See
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/collections.html
Define the CascadeType, otherwise this error appears on saving the
entity: org.hibernate.TransientObjectException: object references an
unsaved transient instance
Make the FetchType EAGER otherwise you
cannot load the form in a different transaction: this error appears:
failed to lazily initialize a collection of role:
ReferenceForm.referenceItems, could not initialize proxy - no Session
I am totally new at this, I am sorry if it is stupid question.
I am trying to design database model for Google App Engine in JPA, but I am unable to get it right. When I find the way I can't get annotations right or I am getting error about M:N not supported in Google App Engine.
I need entity user to have multiple groups and groups have multiple users and there are users who are also group admins.
My basic model was User -> usergroup(user; group; (bool)isAdmin) <-Group
Can somebody give a clean and simple example of how to define relationships?
Please try this.
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private UserGroup usergroup;
}
class userGroup
#Entity
public class UserGroup {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
private String name;
private boolean admin;
#OneToMany(mappedBy = "usergroup", cascade = CascadeType.ALL)
private List<User> users = new ArrayList<User>();
}
please be noticed GAE have limitation on JPA you can read more here
I don't know anything about Google App Engine, but I can help with JPA though.
The problem here is the "isAdmin" column, which prevents the data model to be a simple #ManyToMany relationship with a joiner table.
With the introduction of this field, in the data model you need a Map on the User entity with key=Group and value=isAdmin, similarly you need a corresponding Map in the Group entity in order to know if each User is an admin.
This is modeled with #ElementCollection in the following way:
#Entity
#Table(name="User")
public class User
{
#Id
#GeneratedValue(strategy= GenerationType.TABLE)
private int id;
private String name;
#ElementCollection
#CollectionTable(name="Users_Groups", joinColumns={#JoinColumn(name="userId")})
#MapKeyJoinColumn(name="groupId")
#Column(name="isAdmin")
private Map<Group, Boolean> groups;
}
#Entity
#Table(name="Group")
public class Group
{
#Id
#GeneratedValue(strategy= GenerationType.TABLE)
private int id;
private String name;
#ElementCollection
#CollectionTable(name="Users_Groups", joinColumns={#JoinColumn(name="groupId")})
#MapKeyJoinColumn(name="userId", insertable=false, updatable=false)
#Column(name="isAdmin", insertable=false, updatable=false)
private Map<User, Boolean> users;
}
The important annotation is #ElementCollection, the other annotations are just to name the specific columns of the collection table and make sure they match from both entities: #CollectionTable gives the name of the table and the name of the column representing the id in the current entity. #MapKeyJoinColumn gives the name of the column representing the id of the "key" element in the Map, and #Column gives the name of the "value" element in the map.
I'm not sure if the insertable=false and updatable=false are needed in one of the entities, might avoid adding duplicate rows due to the cyclic dependency between User and Group.
Also you need to manually create the collection table, because at least EclipseLink tries to create it with two "groupId" and "isAdmin" columns. You might consider reviewing the design if it is absolutely needed a cyclic dependency between User and Group.
I am using JPA annotations and when i have relation OneToMany - ManyToOne, when i see my entity in the ManyToOne, the joinColumn is always with null value.
Next i will show my example, i have Product:
#Entity
#Table(name = "PC_PRODUCT")
public class Product extends LaunchEntity {
private static final long serialVersionUID = 1L;
#XmlElement(name = "Product_Name", required = true)
protected String productName;
#XmlElement(name = "Product_Description")
protected String productDescription;
#XmlElement(name = "Product_To_Charge")
#OneToMany(mappedBy = "product", cascade=CascadeType.MERGE)
protected List<ChargeRelation> productToCharge;
And, this is my ChargeRelation class:
#Entity
#Table(name="PC_CHARGE_RELATION")
public class ChargeRelation
extends RelationEntity
{
private static final long serialVersionUID = 1L;
#XmlElement(name = "Charge", required = true)
#OneToOne(cascade = CascadeType.MERGE)
protected Charge charge;
#XmlTransient
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="PRODUCT_ID")
protected Product product;
I am reading a xml file, convert data for a string, make unmarshall for my root object and persist this object.
The problem is when i found a charge relation in my string, the values are inserted on the charge relation table but the column with the product_id is always null.
I have all setters and getters defined. How i can force this to make the manual insert? thanks
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
EclipseLink JAXB (MOXy) has an extension called #XmlInverseReference that allows you to map the back-pointer.
Product
#Entity
#Table(name = "PC_PRODUCT")
public class Product extends LaunchEntity {
#XmlElement(name = "Product_To_Charge")
#OneToMany(mappedBy = "product", cascade=CascadeType.MERGE)
protected List<ChargeRelation> productToCharge;
}
ChargeRelation
The #XmlInverseReference annotation is used where you previously had #XmlTransient. #XmlInverseReference acts like #XmlTransient during the marshal operation, and will populate the back-pointer during an unmarshal operation.
#Entity
#Table(name="PC_CHARGE_RELATION")
public class ChargeRelation extends RelationEntity {
#XmlInverseReference(mappedBy = "productToCharge")
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="PRODUCT_ID")
protected Product product;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Note that JPA provider reflects the state of many-to-one side of relationship when saving it to the database.
However, JAXB only populates one-to-many side during XML unmarshalling, therefore you need to populate many-to-one side manually after unmarshalling.