I am trying to persist Java objects to the GAE datastore.
I am not sure as to how to persist object having ("non-trivial") referenced object.
That is, assume I have the following.
public class Father {
String name;
int age;
Vector<Child> offsprings; //this is what I call "non-trivial" reference
//ctor, getters, setters...
}
public class Child {
String name;
int age;
Father father; //this is what I call "non-trivial" reference
//ctor, getters, setters...
}
The name field is unique in each type domain, and is considered a Primary-Key.
In order to persist the "trivial" (String, int) fields, all I need is to add the correct annotation. So far so good.
However, I don't understand how should I persist the home-brewed (Child, Father) types referenced.
Should I:
Convert each such reference to hold the Primary-Key (a name String, in this example) instead of the "actual" object, so Vector<Child> offsprings; becomes Vector<String> offspringsNames;? If that is the case, how do I handle the object at run-time? Do I just query for the Primary-Key from Class.getName, to retrieve the refrenced objects?
Convert each such reference to hold the actual Key provided to me by the Datastore upon the proper put() operation? That is, Vector<Child> offsprings; becomes Vector<Key> offspringsHashKeys;?
I have read all the offical relevant GAE docs/example. Throughout, they always persist "trivial" references, natively supported by the Datastore (e.g. in the Guestbook example, only Strings, and Longs).
Please see google appengine docs following sections for more clear understanding (Relationships, Transactions)
Also read about detachable objects in JDO
For querying selective columns (or fields), read about fetchgroups in JDO
For your question You have several options:
Owned One to many relationship (the objects will be in same entity group) Here you can have a list of Child in your parent (Father class). This will place all objects in the same entity group. If you do not want to fetch the children every time you fetch Father, you can remove the children from the "default fetch group"
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
#PrimaryKey
#Persistent
private String name;
#Persistent
private int age;
#Persistent(mappedBy = "father", defaultFetchGroup = "false")
private List childern;
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {
#Persistent
#PrimaryKey
private String name;
#Persistent
private Father dad;
}
Unowned relationships where you store the keys instead of references:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
#PrimaryKey
#Persistent
private String name;
#Persistent
private int age;
#Persistent
private List childern;
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {
#Persistent
#PrimaryKey
private String name;
#Persistent
private Key dad;
}
In this case you will have to manage the referential integrity and also make sure they are in the same entity group if you have to update/add them in a single transaction
IMO, if I were modeling a real-world (Father-children) scenario, I'd go the "Owned relatinship" route, since, really, how many children a guy can have ;). Of course there is an additional question of how many fathers are you going to update at a time?
Hope this helps, cheers!
I have examples of creating parent/child relationships using GAE/JPA in my jappstart project. Take a look at how the authentication related entities are related to each other here.
One-to-One (see UserAccount.java and PersistentUser.java):
// parent
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private PersistentUser persistentUser;
// child
#OneToOne(mappedBy = "persistentUser", fetch = FetchType.LAZY)
private UserAccount userAccount;
One-to-Many (see PersistentUser.java) :
#OneToMany(mappedBy = "persistentUser", cascade = CascadeType.ALL)
private Collection<PersistentLogin> persistentLogins;
Many-to-One (see PersistentLogin.java):
#ManyToOne(fetch = FetchType.LAZY)
private PersistentUser persistentUser;
Also, note in the constructors how KeyFactory is used for entities with a parent versus without a parent.
#Id
private Key key;
// this entity has a parent
public PersistentUser(final Key key, final String username) {
this.key = KeyFactory.createKey(key, getClass().getSimpleName(), username);
...
}
// this entity does not have a parent
public UserAccount(final String username) {
this.key = KeyFactory.createKey(getClass().getSimpleName(), username);
....
}
Hopefully, this is helpful for you. I couldn't tell from the question whether you were using JPA or JDO.
If you have a reference to a Father in the Child and to the Children in the Father than you have the potential for inconsistency assuming that the relationship between Father and Child is two-way (ie. every Child's father should be in the list of Children for that Father). Only one of the two references is necessary.
Both solutions will work, but keeping the list of children in the father has two disadvantages:
Every access to the Father object will download the list keys to the child object. If there are many keys, this could cause unnecessary overhead.
I believe that GAE limits the size of a list to 5,000 items.
Related
I have been thinking about the architecture of my server for days since I am developing my first RESTful API from scratch, with Spring Boot.
I am using Hibernate and I created several entities and its relationships using Hibernate/JPA annotations, however, I am not sure whether I should use these entities as the business models since they are "dirty" with the extra fields Hibernate would recommend.
This is my tentative REST API layered architecture
This is an example taken directly from Hibernate's docs.
#Entity(name = "Person")
public static class Person implements Serializable {
#Id
#GeneratedValue
private Long id;
#NaturalId
private String registrationNumber;
#OneToMany(
mappedBy = "person",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PersonAddress> addresses = new ArrayList<>();
// ...
public void addAddress(Address address) {
PersonAddress personAddress = new PersonAddress( this, address );
addresses.add( personAddress );
address.getOwners().add( personAddress );
}
public void removeAddress(Address address) {
PersonAddress personAddress = new PersonAddress( this, address );
address.getOwners().remove( personAddress );
addresses.remove( personAddress );
personAddress.setPerson( null );
personAddress.setAddress( null );
}
}
#Entity(name = "PersonAddress")
public static class PersonAddress implements Serializable {
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Address address;
// ...
}
#Entity(name = "Address")
public static class Address implements Serializable {
#Id
#GeneratedValue
private Long id;
private String street;
#Column(name = "`number`")
private String number;
private String postalCode;
#OneToMany(
mappedBy = "address",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PersonAddress> owners = new ArrayList<>();
// ....
}
What I mean is, if I were to make a class diagram it would not look exactly as these entities because of the conditions you have to accomplish for Hibernate so it can map them to tables with ORM. For example, in a hypotetical class diagram Person would have a list of Address, not a list of PersonAddress as Hibernate suggests in this case (for mapping performance).
My question is whether I should separate the Person Model into two separate entities, one for the business logic layer (services) and one for the data access layer (repositories). Personally, I don't think that's a problem because Hibernate helps me ignore all the table creation, but maybe it's not a good practice and I should separate it into two different entities.
In general I use an API-Model and an Entity- Model.
The api model is used to Exchange data between services and the entity object is used to persist the data. This keeps your architecture more flexible. If something in your busineslogic changes the entity is not automatically affected. Also sometimes you get data by the Client and don‘t want to expose the whole database object. So you can provide just the fields you need and complete the rest in the entity object. This is also recomended by static code analysis sonaqube.
What you are referering to is a split between the persistence model and the business/domain model which is quite common. People often refer to this as the DTO approach.
The approach has many benefits and if you implement it right, almost no downsides.
Implementing it efficiently can be done with Blaze-Persistence Entity-Views a library on top of JPA/Hibernate which will handle all the fetching for you transparently. Take a look at the spring data integration which allows you to get started very quickly or tryout a sample project through an archetype to get a feeling for the benefits.
I have got two entities with Many-to-one (One-to-many) relationship.
Class ParserEntity:
import javax.persistence.*;
#Entity
#Table(name = "parser", schema = "newsmonitoringdb")
public class ParserEntity {
public ParserEntity() {
}
public ParserEntity(String name, SourceTypesEntity type) {
this.name = name;
this.type = type;
}
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Basic
#Column(name = "name")
private String name;
#ManyToOne(optional = false)
#JoinColumn(name="type",referencedColumnName="id")
private SourceTypesEntity type;
...//getters, setters and other fields are here
}
}
Class ParserTypesEntity:
import javax.persistence.*;
import java.util.Collection;
import java.util.List;
/**
* Created by User on 17.08.2016.
*/
#Entity
#Table(name = "source_types", schema = "newsmonitoringdb")
public class ParserTypesEntity {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Basic
#Column(name = "name")
private String name;
#OneToMany(mappedBy="type", fetch=FetchType.EAGER, targetEntity = ParserEntity.class)
private Collection<ParserEntity> siteParser;
...//getters and setters are here
}
}
They have a relation by field and everything seems ok.
However I would like a ParserTypeEntity class to save only names from ParserEntity class in a separate Collection field instead of ParserEntity list.
I would like them to be filled automatically when retrieving types from DB the same way it is done with ParserEntity objects now.
Is there any way to do that, or I have to change the relation to the unidirectional one, get all of the types and then get names for each type by its id?
They have a relation by field and everything seems ok. However I would
like a ParserTypeEntity class to save only names from ParserEntity
class in a separate Collection field instead of ParserEntity list.
I would like them to be filled automatically when retrieving types
from DB the same way it is done with ParserEntity objects now.
I don't think that you can use converter for relation with collections.
Besides, even if you could, I would not use it since it could make the code less natural and less readable. For an enum, it's useful and overall cosmetic but for entity collection, it looks like a hack since it breaks the ORM relationship between entities.
If you use JPA/EntityManager, I can propose a workaround : using a lifecycle event of entities. Otherwise, if you use Hibernate/SessionFactory, you should look listeners for Hibernate. It's the same principle.
The idea is keeping your actual mapping and adding a transient list with data you wish which would be populated in an automatic way when a ParserTypesEntity is loaded.
So, in ParserTypesEntity, you can add a hook method to do a processing when the entity is loaded. In this method, you could collect names of parserEntity collection and add it in a transient List<String>.
public class ParserTypesEntity {
...
#Transient
private List<String> parserTypeNames;
#PostLoad
public void postLoad() {
parserTypeNames = new ArrayList<>();
for (ParserEntity parserEntity : siteParser){
parserTypeNames.add(parserEntity.getName());
}
...
}
I have a class called Member that has a few #OneToMany relationships with other classes. I can insert only one Member with empty OneToMany relationships. Upon inserting the second Member I receive following exception.
ERROR: Duplicate entry '' for key 'UK_7pn834d04yft1rkvpqf0viuyc'
Code
#Entity
public class Member {
#Id
#Column(name = "username", nullable = false, unique = true)
private String email;
#OneToMany(mappedBy = "friend")
private Set<Friendship> friends;
....
To insert Member, I am adding empty set of friends to it. I suspect this and suppose need to have ZeroToMany relationship but could not find its annotation in Hibernate documentation.
Update
Member member = new Member(email, new HashSet<Friendship>());
Constructor
public Member(String email, Set<Friendship> friends) {
super();
this.email = email;
this.friends = friends;
}
toString
member:Member [email=jack.moore1#outlook.com , friends=[]]
console
insert
into
Member
(username)
values
(?)
There is no ZeroToMany relationship. That doesn't exist and doesn't have sense. You can't connect 0 entities with many entities.
You should use #OneToMany relationship here, and you should initialize friends collection
private Set<Friendship> friends = new HashSet<Friendship>();
if you didn't do that in some constructor which is not shown here.
I would suggest to turn on show_sql hibernate property so you can have more details about the problem.
I am trying to insert the relationships between two nodes in Neo4j. I am using the Neo4J(2.1.8 Community) & spring-data-neo4j(3.3.0.RELEASE).
I am using trying to create the Employee-Manager relationship. This relationship entity is Report. (Both class are given below)
But when I am trying to save the relation ship
Employee manager = new Employee("Dev_manager", "Management");
Employee developer = new Employee("Developer", "Development");
developer.setReportsTo(manager);
developer.relatedTo(manager, "REPORTS_TO")
employeeRepository.save(developer);
I am getting exception as
Exception in thread "main" org.springframework.dao.DataRetrievalFailureException: RELATIONSHIP[0] has no property with propertyKey="type".; nested exception is org.neo4j.graphdb.NotFoundException: RELATIONSHIP[0] has no property with propertyKey="type".
Can any one please help me that what is exactly wrong in this code.
The same code works after I change the type of relations in Employee as
#RelatedToVia(type = "REPORT_TO", elementClass = Report.class, direction = Direction.INCOMING)
Note: I am using this reference for this tutorial.
Employee.java class
#NodeEntity
public class Employee {
#GraphId
private Long id;
private String name;
private String department;
#Fetch
#RelatedTo(type = "REPORTS_TO")
private Employee reportsTo; //Employee reports to some employee (i.e. Manager).
#Fetch
#RelatedTo(type = "REPORTS_TO", direction = Direction.INCOMING)
Set<Employee> directReport; //All the employees who reports to this particular this employee.
#Fetch
#RelatedToVia(type = "REPORTS_TO", elementClass = Report.class, direction = Direction.INCOMING)
Set<Report> relations = new HashSet<Report>(); // All the incoming relationship entities.
//*** Constructor, Getter-setters and other methods...
}
Report.java class
#RelationshipEntity(type = "REPORTS_TO")
public class Report {
#GraphId
private Long id;
private String title;
#Fetch
#StartNode
private Employee child;
#Fetch
#EndNode
private Employee parent;
//*** Constructor, Getter-setters and other methods...
}
**Update: **
I have created 2 relations using this class structure. And I got the below result.
It looks like it creates 2 relations between the node. 1 is empty relation using reportsTo(i.e. REPORTS_TO) and another relation using the relations(i.e. REPORT_TO). Can anyone please update why this is happening?
What's the different between: relations and directReport?
I think SDN is just confused with all the duplicate listing of relationships?
Esp. if they are once declared as light relationships without type and once as relationship-entities.
I think for this case it is much clearer and easier to use
template.createRelationshipBetween(employee, manager, "REPORTS_TO");
Or just create, populate and save the relationship-entity Report.
Otherwise you have to make sure that all collections on all sides are consistent with each other.
i have a threading hibernate problem.
I have a ManyToOne Relation with a track whitch include two city objects.
Through the thread it is possible that my trackDao get many trackEntitys to persist witch include the same city (zipcode is unique).
After the trackDao persist the first entity i get a "duplicate entity exception".
Is it possible to configure via annotaition to force a get bevore save to get the existing childId and resume parrent persisting?
Thx
thx for your time.
I try a manual get before i try to save a city to get a possibly already existing city (with the same zipcode) from db.
Now a trip includes two cities.
One city witch is allready stored in db (objectId != null)
and an other city witch have to get persist (objectId == null)
Although hibernate now have to know that city1 is already persist an only city2 have to get persist in city- table i get a "duplicate entity exception". Hibernate will store the existing city again.
To slove that I overrive the generic save method of my tripDao and handle the city persisting manual. -> just save city if city.getId() == null.
So I get a not stored trip object witch includes two already stored citys. If i try to persist that trip object I get the hibernate message
"Error while persist trackjavax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: de....CityEntity"
I belive I had something configured totaly wrong.
Pls help.
I dont want to overrive the save method and store realations manualy. In my opinion that have to works by default, or not?
#Entity
#Table(name = "tracks", uniqueConstraints = #UniqueConstraint(columnNames = { "city1_id", "city2_id" }))
#SessionScoped
public class TrackEntity extends BaseEntity {
private static final long serialVersionUID = 4696847902782174082L;
#ManyToOne(targetEntity = CityEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, optional=false)
#JoinColumn(name="city1_id")
private CityEntity city1;
#ManyToOne(targetEntity = CityEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, optional=false)
#JoinColumn(name="city2_id", referencedColumnName="id")
private CityEntity city2;
private Integer distance;
#Entity
#Table(name = "cities")
#SessionScoped
public class CityEntity extends BaseEntity {
private static final long serialVersionUID = 8823384603378561475L;
private String name;
#Column(unique=true)
private String zipcode;
private String latitude;
private String longitude;
I think a track has two identical city . I had a same problem with Order ->* Product. But this was a bug in application where a Order can not have duplicate product. I don't know what is allowed in your case but I suggest you to Investigate City class and look for equals method. If you have java.util.List in Track class to hold cities then it is possible that that list have two element of same object (==).