I'm writing backend for a cms of sorts that needs to support having different versions of its entities and also the ability to choose which of the versions is the current/active one. I've looked around on this site and others and found some advice on implementing versioning (which I've taken to heart) but nothing in terms of "version switching", as I call it.
My question is twofold, I suppose:
1) I want to know if there is any (preferably open-sourced) system that does something similar? Are there any design patterns that are related to what I'm doing?
2) I would appreciate a review of my implementation below.
Here's a rundown of my entities:
Firm:
#Entity
public class Firm {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
#JoinTable(name = "firm_firm_version")
private FirmVersion firm;
FirmVersion:
#Entity
public class FirmVersion {
#EmbeddedId private FirmVersionId id;
private String name;
#ManyToMany(mappedBy = "firmVersion")
private List<Staff> staff = new ArrayList<>();
FirmVersionId:
#Embeddable
public class FirmVersionId implements Serializable {
private Long id;
private Integer version;
}
FirmStaff:
#Entity
public class FirmStaff {
#Id #GeneratedValue
private Long id;
#ManyToMany
private List<FirmVersion> firmVersion = new ArrayList<>();
private String name;
// ...fields
}
This works fine, but my main concern is the propagation of these #ManyToMany relations. The way I implemented it, every time you update a staff you create 1) a new staff and 2) a new FirmVersion and however many table rows it takes to link them, which I imagine can get out of hand pretty quickly.
Related
I found similar questions, but they did not answer my question.
I have two entities with a many-to-one relationship - unidirectional.
But most importantly, the relationship is lazy. Because it is correct to use a lazy connection, everyone knows it.
Code:
#Entity
public class User implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
#Entity
public class City implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
}
interface BaseEntity {
void setId(Long id);
Long getId();
}
I wrote a method that allows you to search by the transferred fields of the entity.
An example of how this works:
public class Search<T extends BaseEntity> {
public List<T> getByFields(T entity, List<FieldHolder> data) {
// create criteria with passed field name and value by reflection
}
}
class FieldHolder {
private String fieldName;
private Object value;
/**
* "true" - means that the field "value" contains id related object
* "false" - elementary type like: String, Wrapper, Primitive
*/
private boolean isRelationId;
}
The problem is that problems start when you need to search and related objects - by creating related queries.
The following entry is used to send the associated field: "city.id" and the problem is that when I transfer the essence of the related object (City) it is in a proxy and I cannot get id by reflection from City.
My function works perfectly if you specify:
#ManyToOne(fetch = FetchType.EAGER)
private City city;
But it will greatly affect performance, since I have a lot of related objects. Therefore, I want to solve this problem for a lazy load.
I know that this is not an easy task. But perhaps there is some opportunity to somehow get around this problem.
I'm new to JPA. Suppose I have these two entities:
//Imports
#Entity
#Table(name="article", schema = "sch_client")
public class Article implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int price;
private int amount;
//Getters & setters
}
And
#Entity
#Table(name="purchase", schema = "sch_client")
public class Purchase implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
private List<Article> listArticle;}
I want to have something like a purchase contains many articles.
My question is: is it possible with only #OneToMany in Purchase class that points to Article class to have the desired relationship (a purchase contains many articles). Or to use a #OneToMany annotation I have to add a #ManyToOne on Article class. If so, why is is mandatory to add the #ManyToOne? any explanation please.
Thanks in advance.
First of all, I have write a misleading title, I will change it to make it more accurate:
Old title : In JPA, is it possible to use #OneToMany without using #ManyToOne?
New title : #OneToMany does not create the join table.
As I said, I'm new to JPA, my problem can appear dumb, I could delete the question, but I decided to keep it in case someone someday will face similar situation, it can help!
The join table of Purchase and Article was created every time I executed the code very normally, but I didn't notice!, I was checking the logs of NetBeans
and didn't see the join table, I was misled by those logs, I think that a join table doesn't appear in the logs (I hope that someone can confirm this information and make an edit of this answer).
I have created Purchase and Article in a new schema named: sch_sales. and the join table was created in public schema (PostgreSQL).
So, to make it more correct I added schema to #JoinTable as shown below, like this I will have all my tables in the same schema.
#Entity
#Table(name="purchase", schema = "sch_sales")
public class Purchase implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
#JoinTable(name="join_purchase_article", schema = "sch_sales", joinColumns = #JoinColumn(name="sales_fk"), inverseJoinColumns = #JoinColumn(name="article_fk"))
private List<Article> listArticle;
}
UPDATE :
I was having a 3rd table created containing the id of Purchase and Article (a join table) which is obviously not correct.
The normal "behavior" is to have an id_purchase column added in Article, in this page I have find how to have such a result.
To have the desired result, I used the code below:
#Entity
#Table(name="purchase", schema = "sch_sales")
public class Purchase implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
#JoinColumn(name="id_purchase")
private List<Article> listArticle;
}
I have the following mapping:
#Entity
public class Satellite implements Serializable, Comparable<Satellite> {
#NotNull #Id
private long id;
.....
#OrderColumn
#OneToMany(mappedBy = "satellite", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<DataModel> dataModel;
}
and a child entity:
#Entity #IdClass(value=DataModelPK.class)
public class DataModel implements Serializable, Comparable<DataModel> {
private static final long serialVersionUID = -3416403014857250990L;
#Id
private int orbit; // related to reference orbit file
private int dataPerOrbit; // in Gbit
#ManyToOne #Id
private Satellite satellite;
}
originally, DataModel was an embeddable entity, but for a better control over the primary key and the underlying structure of the db, I switched to a more traditional model.
The point is, during the loading of the entity now it generate a stack overflow!! I think there is some cyclic loading between those two entities and it got stuck!
I'm thinking to revert everything back to what it was, but I wish to understand why it gives me this error.
You have #IdClass for DataModel specified to be DataModelPK.class but your #Id annotation is on an int field.
This is a problem, it may be causing you stackoverflow but I am not certain.
Update I now see the second #Id annotation so I stand corrected, I will investigate furtuer.
It took me hours to realize, what the problem is:
I have a Spring Rest service and a GET-Method which returns a user in JSON-Format.
The data comes from my database over sessionFactory.
After debugging it turned out, that the Problem is related to my bidrectional onetomany-manytoone relationship.
So calling
User user = (User) sessionFactory.getCurrentSession().load(User.class, userId);
returns a User-Object where user.getCity().getSupplier() runs into an com.sun.jdi.InvocationException. Therefore Jackson is obviously unable to serialize.
But what causes this exception?
#Entity
#Table(name = "T_CITY")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id;
#OneToMany(mappedBy = "city", cascade=CascadeType.ALL)
private Set<User> user;
#OneToMany(mappedBy = "city", cascade=CascadeType.ALL)
private Set<Supplier> supplier;
User:
#Entity
#Table(name = "T_USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
public User() {
}
#Id
private long id;
#ManyToOne
private City city;
Supplier:
#Entity
#Table(name = "T_SUPPLIER")
public class Supplier implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id;
#ManyToOne
private City city;
As mentioned in the other answer, I think you'll find that your issues are related to the x-to-x relationships. This can sometimes create circular reference issues when trying to jsonify the entity beans.
Sometimes you can avoid or get past this by using annotations, other times a wrapper class is needed. I often just write a wrapper class to handle my JSON transactionts
There are many many references to this type of issues spanning many languages. Here a few starting points for you to research.
Google related search regarding circular references in entity objects
Json and Java - Circular Reference
Circular Dependencies With Jackson
Really like objectify, although still struggling with what is the best way to structure my data in my app. I will be launching a new app soon and do not want to get stuck with a structure that may not function right, or would perform very slowly.
The app will be on the HRD and will have a large number of entity types. For illustrative purposes I will make up some example entities. Suppose the app is for fast food restaurants. Each chain will be an entity(for example McDonalds, Wendy's, etc.). Each specific franchise or location will be an entity as well. Employees, Orders, Menus, Timesheets, and so on will also be entities.
I guess by biggest question is how to setup the relationships between these entities? I have been store relationships by storing the datastore ID as a long in each entity. For example each employee entity would have a long value that is the datastore ID for the location they work at, as well as for which chain they are a member of.
With this structure I can query for all of the orders from a specific restaurant with a statement such as:
Long restaurantId =restaurant.getId();
Query<Order> q=ofy.query(Order.class).filter("location", resturantId);
Just curious if there is any issue with using the datastore/objectify in this manner. Any input would be great! I have been using something similar on a small scale and seems to work fine. Ideally I would like the most efficient structure, and i realize this may take some testing. However once may app is deployed it may be very difficult to change...
#Entity
public class Chain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
//getters & setters, etc
}
#Entity
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long chain; //which resturant chain the location belongs to (mcdonalds, wendy's, etc)
private String address;
private String owner;
private String phoneNumber;
//getters & setters, etc
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long location; //which location the employee works for
private Long chain; //which resturant chain the location belongs to (mcdonalds, wendy's, etc)
private String name;
private String position;
//getters & setters, etc
}
#Entity
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long chain; //which resturant chain the location belongs to (mcdonalds, wendy's, etc)
private Long location;
private Long employee; //employee that took order
private Order order;
private String time;
//getters & setters, etc
}
This is standard practice. Go forth!
Objectify is great - we've been using it for about 6 months and we're very happy with it.
Key<Object> is type safe, Long is not. Using Long is discouraged in the documentation.
Reference: https://github.com/objectify/objectify/wiki/Entities#relationships
I'd encourage you to read through that entire page, it's worth the time. I now have a type-safe structure using Ref<Object> everywhere.