I'm trying to implement a simple GAE service. Particularry I have the Student entity and Category entity. To each Student can be associated one or more Categories. How can I create this relationship using Objectify? THanks
Edit: This is my code. Is It valid?
#Entity
public class Studente {
static long nextID = 17;
public static Key<Studente> key(long id) {
return Key.create(Studente.class, id);
}
List<Key<Categoria>> categorie;
public Studente() {}
#Id Long id;
#Index String nome;
#Index String cognome;
#Index String username;
#Index String password;
public Studente(String nome, String cognome, String username, String password) {
this.nome=nome;
this.cognome=cognome;
this.username=username;
this.password=password;
categorie = new ArrayList<Key<Categoria>>();
}
public static long getNextID() {
return nextID;
}
public static void setNextID(long nextID) {
Studente.nextID = nextID;
}
public List<Key<Categoria>> getCategorie() {
return categorie;
}
public void setCategorie(List<Key<Categoria>> categorie) {
this.categorie = categorie;
}
public void addCategoria(Key<Categoria> k ){
categorie.add(k);
}
}
Create a muti-valued indexed field in Student that holds all Category IDs (or Keys):
#Entity
public class Category {
#Id
public Long id; // auto-generated Id of the Category
}
#Entity
public class Student {
#Id
public Long id; // auto-generated Id of the Student
#Index
public List<Long> categories; // put Category Ids into this list
}
Indexed fields can be used in query filters, so you will be able to search for students that belong to certain category.
I would suggest having a third entity with indexed references to both entities. That way, you could easily query for every student in a category, or for every category of a student.
#Entity
public class Student { /*...*/ }
#Entity
public class Category { /*...*/ }
#Entity
public class StudentCategory {
#Id
private Long id;
#Index
private Ref<Student> student;
#Index
private Ref<Category> category;
/*...*/
}
We have a similar setup in our GAE applications, and it has served us well.
See documentation of Ref<?>.
Related
I'm new to Java and even more newer to Spring (Boot and JPA) but I was curious, I'm trying to debug an issue that says, "No identifier specified for entity".
For illustartion purposes, I've created the following tables from this diagram:
Originally, there was a M:N relationship between the user and vehicle table, so I created an associative entity (UserVehicleAsso) to split the two up. I was following this guide on M:N mapping in Java, http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/
For the most part, it was pretty straight forward but my question is, within the associative entity (UserVehicleAsso), do I have to use the #Id annotation for each of the foreign keys? I assume that I didn't need to because those were automatically generated from each of the respective tables.
Let me know your thoughts or comments, thanks.
Also, below is the code that I used to generate these models:
For the User table/class:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String fName;
private String lName;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="userVehicleAsso",
joinColumns={#JoinColumn(name="userID")},
inverseJoinColumns={#JoinColumn(name="vehicleID")})
private Set<Vehicle> vehicles = new HashSet<Vehicle>();
//constructor
protected User() {}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getFName() {
return fName;
}
public void setFName(String fName) {
this.fName = fName;
}
public String getLName() {
return lName;
}
public void setLName(String lName) {
this.lName = lName;
}
public Set<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(Set<Vehicle> vehicles) {
this.vehicles = vehicles;
}
#Override
public String toString() {
return getFName() + "," + getLName();
}}
For the Vehicle table/class:
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int vehicleId;
private String brand;
private String model;
//foreign key mappings
//mapping with associative
#ManyToMany(mappedBy="vehicles")
private Set<User> users = new HashSet<User>();
//constructors
protected Vehicle() {}
public Vehicle(int id) {
this.vehicleId = id;
}
public Vehicle (String brand, String model) {
this.brand = brand;
this.model = model;
}
/* public Vehicle() {
}*/
public int getVehicleId() {
return vehicleId;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public void setVehicleId(int vehicleId) {
this.vehicleId = vehicleId;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#Override
public String toString() {
// + setBodyType() + "," +
return getBrand() + "," + getModel();
}
}
And then finally, my associtive table/class:
#Entity
public class UserVehicleAsso{
private int userID;
private int vehicleID;
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public int getVehicleID() {
return vehicleID;
}
public void setVehicleID(int vehicleID) {
this.vehicleID = vehicleID;
}
}
In my opinion, it's not necessary to have an Entity class for the middle table in your case. The table will be generated automatically if configured correctly. In this table, there would not be column ID, only two columns with userID and vehicleID data.
Now, if your middle table has more than what are needed to establish the M:N relationship, then your middle Entity class is needed, and the ID of it, too. For example, if this class is intended to store the time stamp every time a relationship is established, you have to:
Create this Entity class,
Give it an ID field with proper generation strategy,
Map the time stamp with a field with adequate type, annotation/XML mapping and so on.
This part of JPA/Hibernate have confused me a lot and I used to get into them. If my memory serves me well this is the proper/perfect way how things should work.
You can specify a composite primary key class that is mapped to multiple fields or properties of the entity.
Here are sample codes:
public class ActivityRegPK implements Serializable {
private int activityId;
private int memberId;
public int getActivityId() {
return activityId;
}
public void setActivityId(int activityId) {
this.activityId = activityId;
}
public int getMemberId() {
return memberId;
}
public void setMemberId(int memberId) {
this.memberId = memberId;
}
}
associtive table/class:
#IdClass(ActivityRegPK.class)
#Entity
#Table(name="activity_reg")
#NamedQuery(name="ActivityReg.findAll", query="SELECT a FROM ActivityReg a")
public class ActivityReg implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="activity_id")
private int activityId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="ins_date")
private Date insDate;
#Id
#Column(name="member_id")
private int memberId;
}
Activity.class
#Entity
#NamedQuery(name="Activity.findAll", query="SELECT a FROM Activity a")
public class Activity implements Serializable {
// some attributes
}
While there is plenty of information around on how to model, in JPA (2), a one-to-one relationship OR an entity having a natural key, I haven't been able to find a clear / simple answer to how to model the situation where we have both, i.e. a one-to-one relationship where the parent table has a natural key. It could obviously be that I might have missed such a tutorial; if so, pointing me to one could also be the answer.
And, as many times with JPA and noobs such as I, the moment one needs a bit more than the most basic model, one can quickly hit the wall.
Hence, considering the following DB model:
What would be the corresponding JPA-annotated object model? (I'm sparing you guys of the things I've tried since I don't want to influence the answer...)
Performance recommendations are also welcome (e.g. "a one-to-many could perform faster", etc.)!
Thanks,
The composite identifier is built out of two numerical columns so the mapping looks like this:
#Embeddable
public class EmployeeId implements Serializable {
private Long companyId;
private Long employeeId;
public EmployeeId() {
}
public EmployeeId(Long companyId, Long employeeId) {
this.companyId = companyId;
this.employeeId = employeeId;
}
public Long getCompanyId() {
return companyId;
}
public Long getEmployeeId() {
return employeeId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof EmployeeId)) return false;
EmployeeId that = (EmployeeId) o;
return Objects.equals(getCompanyId(), that.getCompanyId()) &&
Objects.equals(getEmployeeId(), that.getEmployeeId());
}
#Override
public int hashCode() {
return Objects.hash(getCompanyId(), getEmployeeId());
}
}
The parent class, looks as follows:
#Entity(name = "Employee")
public static class Employee {
#EmbeddedId
private EmployeeId id;
private String name;
#OneToOne(mappedBy = "employee")
private EmployeeDetails details;
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public EmployeeDetails getDetails() {
return details;
}
public void setDetails(EmployeeDetails details) {
this.details = details;
}
}
And the child like this:
#Entity(name = "EmployeeDetails")
public static class EmployeeDetails {
#EmbeddedId
private EmployeeId id;
#MapsId
#OneToOne
private Employee employee;
private String details;
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
this.id = employee.getId();
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
And everything works just fine:
doInJPA(entityManager -> {
Employee employee = new Employee();
employee.setId(new EmployeeId(1L, 100L));
employee.setName("Vlad Mihalcea");
entityManager.persist(employee);
});
doInJPA(entityManager -> {
Employee employee = entityManager.find(Employee.class, new EmployeeId(1L, 100L));
EmployeeDetails employeeDetails = new EmployeeDetails();
employeeDetails.setEmployee(employee);
employeeDetails.setDetails("High-Performance Java Persistence");
entityManager.persist(employeeDetails);
});
doInJPA(entityManager -> {
EmployeeDetails employeeDetails = entityManager.find(EmployeeDetails.class, new EmployeeId(1L, 100L));
assertNotNull(employeeDetails);
});
doInJPA(entityManager -> {
Phone phone = entityManager.find(Phone.class, "012-345-6789");
assertNotNull(phone);
assertEquals(new EmployeeId(1L, 100L), phone.getEmployee().getId());
});
Code available on GitHub.
I am trying to Convert following POJO to a JSON in #RestController:
#Entity
#Table(name="user_location")
#NamedQuery(name="UserLocation.findAll", query="SELECT u FROM UserLocation u")
public class UserLocation implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String addr1;
private String addr2;
private String landmark;
private BigDecimal lat;
private BigDecimal lng;
private String zipcode;
//bi-directional many-to-one association to City
#ManyToOne
private City city;
//bi-directional many-to-one association to State
#ManyToOne
private State state;
public UserLocation() {
}
//Getter - Setters
}
Nested City.java is as follow:
#Entity
#NamedQuery(name="City.findAll", query="SELECT c FROM City c")
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="#id", scope = City.class)
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
//bi-directional many-to-one association to State
#ManyToOne
#JsonIgnore
private State state;
//bi-directional many-to-one association to UserLocation
#OneToMany(mappedBy="city")
#JsonIgnore
private List<UserLocation> userLocations;
public City() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#JsonProperty("state")
public State getState() {
return this.state;
}
public void setState(State state) {
this.state = state;
}
public List<UserLocation> getUserLocations() {
return this.userLocations;
}
public void setUserLocations(List<UserLocation> userLocations) {
this.userLocations = userLocations;
}
public UserLocation addUserLocation(UserLocation userLocation) {
getUserLocations().add(userLocation);
userLocation.setCity(this);
return userLocation;
}
public UserLocation removeUserLocation(UserLocation userLocation) {
getUserLocations().remove(userLocation);
userLocation.setCity(null);
return userLocation;
}
}
Another nested class State.java is as follow:
#Entity
#NamedQuery(name="State.findAll", query="SELECT s FROM State s")
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="#id", scope = State.class)
public class State implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
//bi-directional many-to-one association to City
#OneToMany(mappedBy="state")
#JsonIgnore
private List<City> cities;
//bi-directional many-to-one association to UserLocation
#OneToMany(mappedBy="state")
#JsonIgnore
private List<UserLocation> userLocations;
public State() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public List<City> getCities() {
return this.cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
public City addCity(City city) {
getCities().add(city);
city.setState(this);
return city;
}
public City removeCity(City city) {
getCities().remove(city);
city.setState(null);
return city;
}
public List<UserLocation> getUserLocations() {
return this.userLocations;
}
public void setUserLocations(List<UserLocation> userLocations) {
this.userLocations = userLocations;
}
public UserLocation addUserLocation(UserLocation userLocation) {
getUserLocations().add(userLocation);
userLocation.setState(this);
return userLocation;
}
public UserLocation removeUserLocation(UserLocation userLocation) {
getUserLocations().remove(userLocation);
userLocation.setState(null);
return userLocation;
}
}
The JSON converted from UserLocation.java is as follow:
{
id: 1,
addr1: "11905 Technology",
addr2: "Eden Prairie",
landmark: null,
lat: null,
lng: null,
zipcode: "55344",
city: {
#id: 1,
id: 2,
name: "Westborough",
state: {
#id: 1,
id: 2,
name: "MA"
}
},
state: 1
}
As you can see, the State object is coming as a whole object inside city. But outer state (property of 'UserLocationis showing just an id ofStateobject. I need to have a samestateobject as that ofcity` instead of just id.
I am relatively new to JackSon api. Please advice which approach I should follow to achieve this requirement.
Thanks
This is how jackson designed JsonIdentityInfo annotation logic.
* Annotation used for indicating that values of annotated type
* or property should be serializing so that instances either
* contain additional object identifier (in addition actual object
* properties), or as a reference that consists of an object id
* that refers to a full serialization. In practice this is done
* by serializing the first instance as full object and object
* identity, and other references to the object as reference values.
Jackson will run the full serialization first time and only id will be serialized when it find that object second time.
So, there is two ways how you can fix it:
1) you can simple remove the #JsonIdentityInfo annotation and Jackson will serialize object as you expected but it will remove the #id field from the response. This is probably fine because you still will have 'id' property.
2) I feel like you can simply restructure your objects and delete some references. I would say it is good to do these changes anyway. First of all you can delete reference to the State from UserLocation. I would say that it is not necessary to have the State in userLocation class because of the State is attached to the City.
By doing this you will access State from the City and your problem is solved.
Also I would delete the reference to the list of userLocations from the City class as well as from State class.
It will look like:
UserLocation has City and doesn't have State.
City has State and doesn't have userLocations
State doesn't have userLocations as well as cities.
Hope this helps
First remove that annotations from your State.java and City.java
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="#id", scope = State.class)
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="#id", scope = City.class)
No need of these annotations and in RestController add return type as #ResponseBody UserLocation . It will give you json of that class.
It sounds strange. But i need to validate the field Adress of Company in Person entity.
Company is a field of Person and Addres is afield of Company.
I want to validate the address of Company in Person entity.
Why?? Because The company should have an address if i use it in a Person entity not before?
How can I implement this validation??
#Entity
#Table(schema = Constants.DB_SCHEMA)
public class Person{
Company company;
// Validation of the address should come here ???
public Company getCompany() {
return company;
}
}
#Entity
#Table(schema = Constants.DB_SCHEMA)
public class Company{
String address;
}
Basically you can do that with the annotation #NotNull on the address attribute of your Company class.
But: Your current code won't work anyway because you have not specified the relation between the classes and you don't have primary keys....
Here is an example how it could work:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#OneToOne(cascade= CascadeType.ALL)
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
and
#Entity
public class Company {
#Id
#GeneratedValue
private Long id;
#NotNull
private String adress;
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
I am learning Hibernate and just read the chapter "7.2.3 Adding columns to join tables" of the "Java Persistance with Hibernate" book. My goal is to save Item, Category and CategorizedItem in one transaction.
There is a constructor there (page 305):
public CategorizedItem(String username, Category category, Item item) {
// Set fields
this.username = username;
this.category = category;
this.item = item;
// Set identifier values
this.id.categoryId = category.getId();
this.id.itemId = item.getId();
// Guarantee referential integrity
category.getCategorizedItems().add(this);
item.getCategorizedItems().add(this);
}
It accepts category and item objects. If I create a Category and an Item and want to connect them with this technique, they obviously have to be persisted BEFORE, as category.getId() and item.getId() return null.
Is there "a trick in the Hibernate bag" that can cascade the saving of a join table? The join table have additional columns. I want to save all three objects in the onSuccess handler in my web page controller. All three entities or none of them must be inserted.
You said
My goal is to save Item, Category and CategorizedItem in one transaction
Is there a trick in the Hibernate bag that can cascade the saving of a join table ?
Yes, use MutableInt
#Entity
public class CategorizedItem implements Serializable {
private CategorizedItemId categorizedItemId;
private String userName;
private Category category;
private Item item;
/**
* required no-arg constructor
*/
public CategorizedItem() {}
public CategorizedItem(CategorizedItemId categorizedItemId) {
this.categorizedItemId = categorizedItemId;
}
public CategorizedItem(String userName, Category category, Item item) {
this.userName = userName;
this.categorizedItemId = new CategorizedItemId(category.getIdAsMutableInt(), item.getIdAsMutableInt());
}
#EmbeddedId
public CategorizedItemId getCategorizedItemId() {
return this.categorizedItemId;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="CATEGORY_ID", insertable=false, updateable=false)
public Category getCategory() {
return this.category;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ITEM_ID", insertable=false, updateable=false)
public Item getItem() {
return this.item;
}
// setter's goes here
/**
* It MUST implements Serializable
* It MUST overrides equals and hashCode method
* It MUST has a no-arg constructor
*
* Hibernate/JPA 1.0 does not support automatic generation of compound primary key
* You SHOULD set up manually
*/
#Embeddable
public static class CategorizedItemId implements Serializable {
private MutableInt categoryId = new MutableInt(-1);
private MutableInt itemId = new MutableInt(-1);
/**
* required no-arg constructor
*/
public CategorizedItemId() {}
public CategorizedItemId(MutableInt categoryId, MutableInt itemId) {
this.categoryId = categoryId;
this.itemId = itemId;
}
#Column(name="CATEGORY_ID", updateable=false, nullable=false)
public Integer getCategoryId() {
return this.categoryId.intValue();
}
public void setCategoryId(Integer categoryId) {
return this.categoryId.setValue(categoryId);
}
#Column(name="ITEM_ID", updateable=false, nullable=false)
public Integer getItemId() {
return this.itemId.intValue();
}
public void setItemId(Integer itemId) {
return this.itemId.setValue(itemId);
}
// getter's and setter's
#Override
public boolean equals(Object o) {
if(!(o instanceof CategorizedItemId))
return null;
finalCategorizedItemId other = (CategorizedItemId) o;
return new EqualsBuilder().append(getCategoryId(), other.getCategoryId())
.append(getItemId(), other.getItemId())
.isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder().append(getCategoryId())
.append(getItemId())
.toHashCode();
}
}
}
Here goes Category
#Entity
public class Category implements Serializable {
public MutableInt id = new MutableInt(-1);
private List<CategorizedItem> categorizedItemList = new ArrayList<CategorizedItem>();
#Transient
public MutableInt getIdAsMutableInt() {
return this.id;
}
#Id
#GeneratedValue
public Integer getId() {
return this.id.intValue();
}
public void setId(Integer id) {
return this.id.setValue(id);
}
#OneToMany(mappedBy="category")
#JoinColumn(name="CATEGORY_ID", insertable=false, updateable=false)
#Cascade(CascadeType.SAVE_UPDATE)
public List<CategorizedItem> getCategorizedItemList() {
return categorizedItemList;
}
// setter's
/**
* Use this method when you have a saved Item
*/
public void addCategorizedItem(CategorizedItem categorizedItem) {
categorizedItem.setCategorizedItemId(new CategorizedItemId(getIdAsMutableInt(), categorizedItem.getItem().getIdAsMutableInt()));
categorizedItem.setCategory(this);
getCategorizedItemList().add(categorizedItem);
}
}
And Item
#Entity
public class Item implements Serializable {
public MutableInt id = new MutableInt(-1);
private List<CategorizedItem> categorizedItemList = new ArrayList<CategorizedItem>();
#Transient
public MutableInt getIdAsMutableInt() {
return this.id;
}
#Id
#GeneratedValue
public Integer getId() {
return this.id.intValue();
}
public void setId(Integer id) {
return this.id.setValue(id);
}
#OneToMany(mappedBy="item")
#JoinColumn(name="ITEM_ID", insertable=false, updateable=false)
#Cascade(CascadeType.SAVE_UPDATE)
public List<CategorizedItem> getCategorizedItemList() {
return this.categorizedItemList;
}
// setter's
/**
* Use this method when you have a saved Category
*/
public void addCategorizedItem(CategorizedItem categorizedItem) {
categorizedItem.setCategorizedItemId(new CategorizedItemId(getIdAsMutableInt(), categorizedItem.getCategory().getIdAsMutableInt()));
categorizedItem.setItem(this);
getCategorizedItemList().add(categorizedItem);
}
}
Now because you need categoryId and itemId before saving CategorizedItem, do as follows
Category category = new new Category();
Item item = new Item();
session.save(new Category());
session.save(new Item());
session.save(new CategorizedItem(userName, category, item));
Notice the cascading just works when you have either a saved Category or a saved Item. Otherwise, you need to follow the approach shown above