In my spring data project, there is an entity that looks like:
public class Employee {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="category")
private Category category;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="element_id")
private Department department;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="agency_id")
private Agency agency;
public Employee() {}
// routine getters and setters
}
In my EmployeeRepository, I find that I can derive a method, findEmployeeById() that seems to work in exactly the same way as the usual findById() (but I'm not sure). Can someone explain the difference between these two methods?
Optional<Employee> findEmployeeById (Integer id);
vs
Optional<Employee> findById (Integer id);
I "discovered" this by accident via autocomplete in my side (IntelliJ).
The difference is that while findEmployeeById() searches - as it states - employee by its field named id, findById searches by field annotated as #Id disregarding what is the name of the entity's id field.
In your case - as with many else's - the thing is that the #Id field happens to be named id so the result is the same.
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.
I'm mapping a relationship that does not use the entity's primary key. Using "referencedColumnName" with a column different than the primary key causes hibernate to eagerly fetch the association, by issuing an extra select, even when it's tagged with FetchType.LAZY.
My goal is to make it behave like a regular mapping, meaning it wouldn't issue an extra query every time I need to query the main entity.
I have already tried using #LazyToOne(LazyToOneOption.NO_PROXY), which sorts out the problem, but it does not operate well with Jackson's (JSON parsing library) module "jackson-datatype-hibernate5", which skips hibernate lazy proxies when serializing the results.
Here is a scenario almost like the one I have that causes the problem:
Entities:
#Entity(name = "Book")
#Table(name = "book")
public class Book
implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
#NaturalId
private String isbn;
//Getters and setters omitted for brevity
}
#Entity(name = "Publication")
#Table(name = "publication")
public class Publication {
#Id
#GeneratedValue
private Long id;
private String publisher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "isbn",
referencedColumnName = "isbn"
)
private Book book;
#Column(
name = "price_in_cents",
nullable = false
)
private Integer priceCents;
private String currency;
//Getters and setters omitted for brevity
}
Repository (Spring-Data, but you could try directly with the EntityManager):
#Repository
public interface PublicationRepository extends JpaReadRepository <Publication, Long>
{
#Query ("SELECT d FROM Publication d WHERE d.publisher = ?1 ")
Optional <Publication> findByPublisher (String isbn);
}
Thanks
The only way to achieve what you are looking for is by moving the annotatation #Id to the isbn property.
You can leave the #GeneratedValue on the autoincrement property.
Notes:
1 - Make sure that your equals/hc are following the OID(Object ID) on your domain case the "NaturalId" ISBN.
2 - It will be good to ensure if possible on DB level that your natural ID has unique contraint on it.
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 have entity called Shop and different types of shops (sport, clothes, tech...). That list of types will probably be predefined. One shop can have multiple types. What is the best way to represent that?
I created two entities Shop and Type.
#Entity
#Table(name = "store")
public class Store {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
...
}
#Entity
#Table(name = "type")
public class Type {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
private String name; //sport, clothes, tech...
}
What type of relationship between these two entities should I use?
Given that you said Type is probably predefined, it seems more reasonable to model it as enum, and making use of ElementCollection
(I have to admit that I haven't tried to use them both in combination, I believe it should work though :P )
public enum Type {
SPORT, CLOTHES, TECH
}
public class Shop {
#Id
private Long id;
#ElementCollection
#CollectionTable(
name="SHOP_TYPE",
joinColumns=#JoinColumn(name="SHOP_ID")
)
#Column(name="TYPE")
// mapping for enum by usertype or other way, depending on JPA version you are using
private List<Type> types;
}
Of course, you can model SHOP_TYPE as an entity (e.g. ShopType) if you want more complicated operations on it, but what described above looks to me a more reasonable domain model.
Even you do not want the Type to be predefined (i.e. you can create whatever type in your application), it is still more reasonable to model it as a ManyToMany relationship:
public class Type {
#Id
#Column(name="TYPE_ID")
private Long id
#Column(name="TYPE_NAME")
private String name;
}
public class Shop {
#Id
#Column(name="SHOP_ID")
private Long id;
#ManyToMany
#JoinTable(
name="SHOP_TYPE",
joinColumns=#JoinColumn(name="SHOP_ID"),
inverseJoinColumns=#JoinColumn(name="TYPE_ID"))
private List<Type> types;
}
Just one thing to note: It does not look right to have a Type entity which contains a String as type name, and refer to Shop (as some of the answer suggested). Type should be an entity of itself, and, for example, different shops having CLOTHES type should refer to same CLOTHES entity (unless you view types as some kind of arbitrary tag)
The Store and Type many to many relationship is linked with a third / join table named STORE_TYPE_MAPS.
Store Entity:
#Entity
#Table(name = "store")
public class Store {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
#ManyToMany(fetch = FetchType.LAZY, targetEntity=Type.class)
#JoinTable(name="STORE_TYPE_MAPS",
joinColumns=#JoinColumn(name="STORE_ID"),
inverseJoinColumns=#JoinColumn(name="TYPE_ID")
private Set<Type> types;
//... getter-setter
}
If Type is an Entity then make it ManyToMany
#ManyToMany
#JoinTable(name="Store_Type")
private List<Type> types;
also it can be an enum
I rather prefer to create a new entity called ShopType, the ManyToMany relationship will be created as explained below.
This new Entity allows you to have extra columns in the join table, "ShopType", (which can't be done with a simple #ManyToMany). For example, you can add this information: "the number of articles of each type in each shop".
The code is as follows:
public class Shop {
#Id
#Column(name="SHOP_ID")
private Long id;
#OneToMany(mappedBy = "shop", cascade = CascadeType.ALL)
private List<JoinAchatType> joinShopType = new ArrayList();
}
public class ShopType {
#ManyToOne
#JoinColumn(name = "SHOP_ID")
private Shop shop;
#ManyToOne
#JoinColumn(name = "TYPE_ID")
private Type type;
private int numberArticle;
}
public class Type {
#Id
#Column(name="TYPE_ID")
private Long id;
#OneToMany(mappedBy = "type", cascade = CascadeType.ALL)
private List<JoinAchatType> joinShopType = new ArrayList();
}
For more information check these links:
Mapping many-to-many association table with extra column(s)
.
The best way to use the #ManyToMany annotation with JPA and Hibernate.