JPA Datanucleus showing old data that is no longer in datastore - google-app-engine

I have two entities: Account, Home. The relationship is such
#Entity
public class Account implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key userId;
private String data;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Home> homes = new ArrayList<Home>();
public Account() {
}
public Account(String data) {
this. data = data;
}
public Account(String data, List<Home> homes) {
super();
this. data = data;
this.homes = homes;
}
public List<Home> getHomes() {
return homes;
}
public void setHomes(List<Home> homes) {
this.homes = homes;
}
}
//HOME
#Entity
public class Home implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private long timestamp;
private long userId;
public Home(long latlong, long userId) {
Key key = KeyFactory.createKey(Home.class.getSimpleName(), latlong + "" + userId);
this.key = key;
this.userId = userId;
}
//getters and setters
}
The invariants are such that the latlong for a user never changes. Therefore, when I add a Home entity, if one already exists for said user, it is simply replaced in the datastore. So far so good. But I am getting a problem for the following user case:
Say user X edit his home data 6 times. Sure enough when I look in the datastore, there is only one entry for Home and it contains the latest data. But after querying for Account using datanucleus, when I do getHomes().size() the result is 6. Apparently datanucleus is caching every single edit as an individual entity. It’s a mystery to me. I am logging the code so I am seeing it happening: For the exact same key (I use a for-loop), I am getting each iteration/edit of the entity. How do I keep this from happening? The datastore clearly shows one entity for the key. I just want that one entity: no historical list.
My datanucleus/jpa getById is
#Override
public T getById(Long id) {
EntityManager mgr = getEntityManager();
try {
return null == id ? null : mgr.find(type, id);
} finally {
mgr.close();
}
}

Still baffled per the root cause of the problem, I replaced List with Set and my problem is now solved.

Related

How to update a List of Users in the User class Spring Boot

I have a Java User class, a user can have friends (List<User>). By default, Hibernate create two tables : USER and USER_FRIENDS(USER_ID,FRIENDS_ID)
The problem is when I change friends in my code and that I save(user), spring add the new friends but don't remove in the database the friends removed from the array list.
#Entitypublic class User implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String pseudo;
private String password;
private String email;
private Gender gender;
#Lob
private byte[] avatar;
private String description;
private Date birthdate;
#ManyToMany(cascade = CascadeType.ALL)
private List<Game> favoriteGames = new ArrayList<>();
#OneToMany( cascade = CascadeType.ALL)
private List<User> friends = new ArrayList<>();
I tried #ManyToMany, #OneToMany, cascade = CascadeType.ALL
Basically, first I would advise that you take special care with your equals and hashCode implementation in your entities. You did not show us that, but it should be something like this in your User.java:
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User other = (User) o;
return id != null && id.equals(other.getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
Those are very important, especially when working with entities in collections.
Secondly, a connection between a User and his Friends (other Users) should be modeled as Many-to-Many, because:
every user can be a friend to MANY of other users
every user can have any number of friends, in other words MANY friends
And I would model this connection like this:
#ManyToMany
#JoinTable(name = "user_friends", joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "friend_user_id"))
private Set<User> friends = new HashSet<>();

Hibernate: Entity xx with id xx not found, one to one relationship

I am trying to use Hibernate for the first time, so I am not very familiar with the syntax yet. I am trying to model a library system. The classes that are causing problem are "Borrow" and "CopyBook" which are as following:
import javax.persistence.*;
#Entity
#Table(name = "copybook", schema="project")
public class CopyBook {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="copy_id", unique = true, nullable = false)
private int copyId;
#Convert(converter = StatusAttributeConverter.class)
#Column(name="status", columnDefinition = "TEXT DEFAULT 'AVAILABLE'")
private Status status;
#ManyToOne(optional = false)
#JoinColumn(name = "book_id")
private Book book;
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
#OneToOne(mappedBy = "copy")
private Borrow borrow;
public Borrow getBorrow() {
return borrow;
}
public void setBorrow(Borrow borrow) {
this.borrow = borrow;
}
#OneToOne(mappedBy = "copy", optional = false)
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "borrow", schema = "project")
public class Borrow {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="no_borrow", unique = true, nullable = false)
private int noBorrow;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="date_borrow", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = false, nullable = false)
private Date dateBorrow;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="date_return")
private Date dateReturn;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="max_date_return", insertable = false, updatable = false)
private Date maxDateReturn;
#OneToOne(optional = false)
#JoinColumn(name = "copy_id")
#NotFound(action = NotFoundAction.IGNORE)
private CopyBook copy;
public CopyBook getCopy() {
return copy;
}
public void setCopy(CopyBook copy) {
this.copy = copy;
}
#ManyToOne(optional = false)
#JoinColumn(name="mem_id")
private Members member;
public Members getMember() {
return member;
}
public void setMember(Members member) {
this.member = member;
}
}
I am using IntelliJ, so when I try to run the query of the Borrow entity it showed me that it cannot find CopyBook with id 1 even though it actually exists in the database
This is the entries currently present in my database (In these two table)
CopyBook:
Borrow:
As we can see, there is indeed a CopyBook with id of 1. This is also proved when I run the query of CopyBook entity which successfully returned all the results
Result after running in JPA select entity from CopyBook entity
Just to see what results Borrow is actually giving, I have added #NotFound(action = NotFoundAction.IGNORE). And this is the result I get
So I found this is a very bizarre situation because all data exist, and CopyBook is able to find its correspondent Borrow. But Borrow is unable to find its correspondent CopyBook ?
Strangley, I have another entity class Order which has almost the same attributes (Also has a OneToOne relationship with CopyBook), and it works perfectly.
Problem Solved.
Because I have multiple One-To-Many relationship, and the FetchType is by default Eager. Whenever I want to retrieve the entity, it will perform Left Outer Join for all the entities until the end.
However, under the scope of this context. A book does not have to be ordered to exist in the library, be performing "JOIN" it loses entries and therefore causes the aforementioned problem.
As a result, all I did was to set the attributesOrder and Borrow with optional = true and fetch = Fetchtype.Lazy so it will not perform join unless necessary

How to fetch data from database using hibernate ManyToMany

I'm using hibernate with manyToMany relation and I want to display data from database
Thank you in advance.
I get this errors:
database :
Here is the code :
Class EnseignerId :
#Embeddable
public class EnseignerId implements Serializable {
//id professeur
#Column(name="professeur_code")
private int code;
//id matiere
#Column(name="matiere_reference")
private String reference;
public EnseignerId() {
super();
}
//getters and setters...
Class Enseigner :
#Entity
#Table(name="Enseigner")
public class Enseigner {
#EmbeddedId
private EnseignerId id = new EnseignerId();
//id prof
#ManyToOne
#MapsId("code")
private Professeur professeur;
//id matiere
#ManyToOne
#MapsId("reference")
private Matiere matiere;
#Column(name="heures")
private int heures;
//constructor getters and setters...
Class Professeur:
#Entity
#Table(name="professeur")
public class Professeur {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="code")
private int code ;
#Column(name="nom")
private String nom;
#Column(name="prenom")
private String prenom;
...
#OneToMany(
mappedBy="professeur",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Enseigner> matieres; //List<Association> Class; //I followed a tutorial
//constructor getters and setters...
public List<Enseigner> getMatieres() {
return matieres;
}
Class Matiere :
#Entity
#Table(name="matiere")
public class Matiere {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="reference")
private String reference;
#Column(name="description")
String description;
#Column(name="volume")
int volume;
#OneToMany(
mappedBy= "matiere",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Enseigner> professeurs;
//constructor getters and setters...
getProfesseur() method :
public Professeur getProfesseur(int code) {
SessionFactory sessionFactory = getSessionFactory(); //static method
Session session = sessionFactory.openSession();
Professeur professeur = null;
try {
session.getTransaction().begin();
System.out.println("------------Calling getProfesseur()----------");
professeur = session.get(Professeur.class, code);
if(professeur != null) {
System.out.println(professeur);
}else {
throw new DAOException( "CODE INVALIDE!" );
}
}
catch(Exception e ) {
System.out.println(e.getMessage());
}
finally {
session.close();
}
return professeur;
}
Saving data and getting professors who don't have an Matiere work. but getting Matiere or professeur whose primary key exists in the join table Enseigner generate errors when I do something like :
Professeur prof =profDAO.getProfesseur(2); //*generates errors* //the professor with id=2 exists in database
System.out.println(prof);
List<Enseigner> enseigner = prof.getMatieres(); //*generates errors*...
List<Matiere> matieres = new ArrayList<>();
for(Enseigner ens : enseigner) {
matieres.add(ens.getMatiere());
System.out.println(ens);
}
/*for(Matiere mat : matieres) {
System.out.println(mat);
}*/
This problem has nothing to do with Hibernate. Please inspect the stack trace carefully: your Enseigner.toString() calls Professeur.toString() which in turn calls Enseigner.toString() again and so on.
I notice this problem more and more these days when people blindly use Lombok with its #Data (which should almost never be used), #ToString and #EqualsAndHashCode. These generate respective methods that include all fields!
You need to remove these annotations or set them up so that they use only the fields that you really need. Most of the time your equals() and hashCode() are not needed when you write web apps with ORM. Hibernate ensures you don't have 2 instances of the same entity.
On the other hand toString() can be useful, but we shouldn't include all fields in it - just the ones that are helpful in identifying the entity.
You have cyclic reference. You need exclude field professeurs and matieres by #JsonIgnoreProperties

Error in Many to one relation

My Organization entity
#Entity
public class Organization implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String name;
private String type;
private byte image;
#OneToMany(cascade=CascadeType.MERGE)
#JoinColumn(name="ORGANIZATION_ID")
private List<User> admin;
My User entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String score;
private boolean online;
private String resume;
public Status status;
public enum Status {
ACTIVE, INACTIVE, VERIFIED, NOT_VERIFIED , BANNED
};
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="ORGANIZATION_ID")
private Organization organization;
#Persistent
private User_personal user_p;
public User_personal getUser_personal(){
return user_p;
}
public void setUser_personal(User_personal user_p) {
this.user_p = user_p;
}
#OneToMany(mappedBy = "user", cascade=CascadeType.MERGE)
private List<Project> projects;
I got the one-Many relation between User and Projects correctly but not working for User and Organization(many-one).I am getting error like this
WARNING: /OrganizationServlet
javax.persistence.PersistenceException: Detected attempt to establish
Organization(no-id-yet) as the parent of User(4793870697103360) but the
entity identified by User(4793870697103360) has already been persisted
without a parent. A parent cannot be established or changed once an object
has been persisted.at...
showing error at em.getTransaction().commit();.
My servlet is
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
// TODO Auto-generated method stub
HashMap<String,String> map = Request_to_map.getBody(request);
boolean validToken = JWT.parseJWT(request.getHeader("Access-token")
,map.get("email"));
JsonObject output = new JsonObject();
List<User> organization_admin = new ArrayList<User>();
if(validToken == true){
EntityManager em;
em = EMF.get().createEntityManager();
String organizationName = map.get("name");
String type = map.get("type");
byte image = 0;
if(map.get("image")!=null)
{
image = Byte.valueOf(map.get("image"));
}
String email = map.get("email");
if(organizationName==null||type==null||email==null||map.get("image")==null)
{
throw new IllegalArgumentException("please fill required
details");
}
try{
em.getTransaction().begin();
User user = User.find(email, em);
if(user!=null)
{
Organization.org_status status= org_status.ACTIVE;
Organization organization = new
Organization(organizationName, type,image,status);
user.setOrganization(organization);
organization_admin = organization.getAdmin();
if(organization_admin == null)
{
organization_admin = new ArrayList<User>();
}
organization_admin .add(user);
organization.setAdmin(organization_admin);
em.persist(organization);
em.persist(user);
output.addProperty("message", "done");
em.getTransaction().commit();
}
else
output.addProperty("message","No such User found.Please
check details provided");
}
finally{
if(em.getTransaction().isActive())
em.getTransaction().rollback();
// em.close();
}
}
else
output.addProperty(Constants.MESSAGE,
Constants.TokenNotAuthenticated);
response.setContentType("application/Json");
response.getWriter().println(output);
}
Can anyone help me in getting this? When user is created I am getting ORGANIZATION_ID as a column but cant create entity of organization.I dont think joins are to be used as GAE doesn't allow it.

How do I annotate a collection of embedded objects in Google App Engine (Java)?

Is it possible to store a collection of embedded classes in Google App Engine (Java)? If so, how would I annotate it?
This is my class:
#PersistenceCapable
public class Employee
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key key;
#Persistent(embeddedElement = "true")
private List<ContactInfo> contactDetails = new ArrayList<ContactInfo>();
public void addContactInfo(String streetAddress, String city)
{
this.contactDetails.add(new ContactInfo(streetAddress, city));
}
public List<ContactInfo> getContactDetails()
{
return this.contactDetails;
}
#PersistenceCapable
#EmbeddedOnly
public static class ContactInfo
{
#Persistent
private String streetAddress;
#Persistent
private String city;
public ContactInfo(String streetAddress, String city)
{
this.streetAddress = streetAddress;
this.city = city;
}
public String getStreetAddress()
{
return this.streetAddress;
}
public String getCity()
{
return this.city;
}
}
}
Here is the test code:
// Create the employee object
Employee emp = new Employee();
// Add some contact details
emp.addContactInfo("123 Main Street", "Some City");
emp.addContactInfo("50 Blah Street", "Testville");
// Store the employee
PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
pm.makePersistent(emp);
}
finally
{
pm.close();
}
// Query the datastore for all employee objects
pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery(Employee.class);
try
{
List<Employee> employees = (List<Employee>) query.execute();
// Iterate the employees
for(Employee fetchedEmployee : employees)
{
// Output their contact details
resp.getWriter().println(fetchedEmployee.getContactDetails());
}
}
finally
{
query.closeAll();
pm.close();
}
The test code always outputs 'null'. Any suggestions?
I've tried annotating the contactDetails list as #Embedded, but the DataNucleus enhancer threw an exception. I've also tried adding defaultFetchGroup = "true" to the #Persistent annotation on the contactDetails list (in case it was a problem with fetching the data), but the output was still null. I've examined the employee object in the console and there's no evidence of any contact detail fields being stored on the object, nor are there any stand-alone ContactInfo objects in the datastore (not that I'd expect there to be, given they are embedded objects).
Is this even possible to do? I know that the fields of an embedded class are stored as properties on the entity, so I can see how field name conflicts might arise in a collection scneario, but the App Engine docs don't explicitly say it cannot be done.
I don't know if you need the answer still but putting the List into the defaultFetchGroup solved this issue for me:
#PersistenceCapable
public class Employee
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key key;
#Persistent(embeddedElement = "true", defaultFetchGroup = "true")
private List<ContactInfo> contactDetails = new ArrayList<ContactInfo>();
// Snip...
}
With JDO you would do it as per the DataNucleus docs
http://www.datanucleus.org/products/accessplatform_2_2/jdo/orm/embedded.html#Collection
The example shows XML, but the annotation names are approximately equivalent.
Whether GAE/J's plugin supports this I couldn't say since it's Google's responsibility. Maybe try it?

Resources