I have some JPA models: "Category" and "Article":
#Entity
#Table(name = "categories")
public class Category {
private int id;
private String caption;
private Category parent;
private List<Category> childrenList;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
#ManyToOne
#JoinColumn(name = "parent_id")
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
#OneToMany
#JoinColumn(name = "parent_id")
public List<Category> getChildrenList() {
return childrenList;
}
public void setChildrenList(List<Category> childrenList) {
this.childrenList = childrenList;
}
}
#Entity
#Table(name = "articles")
public class Article {
private int id;
private String caption;
private boolean isAvailable;
private String description;
private int price;
private Category category;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
#Column(name = "is_available")
#Type(type = "org.hibernate.type.NumericBooleanType")
public boolean getIsAvailable() {
return isAvailable;
}
public void setIsAvailable(boolean available) {
isAvailable = available;
}
#Column
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Column
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
#ManyToOne
#JoinColumn(name = "category_id")
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
Also i have some REST controller with two methods:
1)In the first method i need to get and serialize last 10 Articles, but i don't need "childrenList" and "parent" field in Categegory.
2)In the second method i need to get the same but serialize "parent" field.
How can i solve this?
If i will use #JsonIgnore annotation to these fields then they will be never serialized.
Or should i use DTO classes?
How can i dynamically set field for ignoring?
I never use my Entitys for generating JSON, I think another set DTO classes will make you happier in the long run. My DTO typically has a constructor which takes the Entity as argument (it still needs a default constructor if you plan to use it for parsing incoming JSON).
If you really want to use your Entities, I would recommend that you use MixIns, which allows you to register a MixIn class, that augments the serialization of a specific class.
Here is a link to a MixIn example I made for another answer.
Use a custom serializer, the psedo code is below.
public class CategorySerializer extends StdSerializer<Category> {
public CategorySerializer() {
this(null);
}
public CategorySerializer(Class<Category> t) {
super(t);
}
#Override
public void serialize(Category value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
// put the logic here to write the parent and child value or not
// here is the example to how the data is serialized
jgen.writeStartObject();
jgen.writeNumberField("id", value.id);
jgen.writeStringField("caption", value.caption);
jgen.writeEndObject();
}
}
Now, to utilize the custom serializer put this annotation above your Catagory entity class.
#JsonSerialize(using = CategorySerializer.class)
Related
I have a next question: while working with Hibernate 3.3.0 run into a situation when I have two tables with one-to-many relationships and I need to get the list of parents. In each entity must be filled the several fields from the parent table and a list of all children mapped in the parent. For the easiest understanding, I give an example. I have two tables with one-to-many relationships: parent is "recipients" and child is "requisites". And I have two classes whose objects are the rows of these tables. Class for the table of recipients:
#Entity
#Table(name = "recipients")
#JsonFilter(value = "recipientsFilter")
public class POJORecipient implements POJO {
private static final long serialVersionUID = 4436819032452218525L;
#Id
#Column
private long id;
#Version
#Column
private long version;
#Column(name = "client_id")
private long clientId;
#Column
private String inn;
#Column
private String name;
#Column(name = "rcpt_country_code")
private String rcptCountryCode;
#Column(name = "rcpt_passp_ser")
private String rcptPasspSer;
#Column(name = "rcpt_passp_num")
private String rcptPasspNum;
#OneToMany(mappedBy = "recipient", fetch = FetchType.LAZY)
#JsonManagedReference
private Set<POJORequisite> requisites = new HashSet<>();
public POJORecipient(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public long getClientId() {
return clientId;
}
public void setClientId(long clientId) {
this.clientId = clientId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getRcptCountryCode() {
return rcptCountryCode;
}
public void setRcptCountryCode(String rcptCountryCode) {
this.rcptCountryCode = rcptCountryCode;
}
public String getRcptPasspSer() {
return rcptPasspSer;
}
public void setRcptPasspSer(String rcptPasspSer) {
this.rcptPasspSer = rcptPasspSer;
}
public String getRcptPasspNum() {
return rcptPasspNum;
}
public void setRcptPasspNum(String rcptPasspNum) {
this.rcptPasspNum = rcptPasspNum;
}
public Set<POJORequisite> getRequisites() {
return requisites;
}
public void setRequisites(Set<POJORequisite> requisites) {
this.requisites = requisites;
}
}
and for requisites table:
#Entity
#Table(name = "requisites")
public class POJORequisite implements POJO {
private static final long serialVersionUID = -35864567359179960L;
#Id
#Column
private long id;
#Version
#Column
private long version;
#Column
private String bic;
#Column
private String bill;
#Column
private String comments;
#Column
private String note;
#ManyToOne
#JoinColumn(name = "recipient_id")
#JsonBackReference
private POJORecipient recipient;
public POJORequisite(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public String getBic() {
return bic;
}
public void setBic(String bic) {
this.bic = bic;
}
public String getBill() {
return bill;
}
public void setBill(String bill) {
this.bill = bill;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public POJORecipient getRecipient() {
return recipient;
}
public void setRecipient(POJORecipient recipient) {
this.recipient = recipient;
}
}
So, I want to select from the recipients only names and all mapped requisites. Consequently, after the selection, I will have a list of POJORecipient objects and in each object filled only the field "name" and set of POJORequisite objects.
As answer of my question I want to discover one of next: how can I do that with help HQL or Criteria API (the second variant is preferable), or understand it is impossible in Hibernate at all, or that this possibility appeared in later versions (also preferably with example). I'm trying to resolve this question for several months now and will be immensely grateful for any help. All clarifications and advices also will be so helpful. Thanks in advance!!!
I have entity as follow.
#Entity
#Table(name = "BankProduct")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private ProductUseType type;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne
private ProductSerial serial;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductUseType getType() {
return type;
}
public void setType(ProductUseType type) {
this.type = type;
}
public ProductSerial getSerial() {
return serial;
}
public void setSerial(ProductSerial serial) {
this.serial = serial;
}
}
My controller is :
#RestController
public class DEmoController {
#Autowired
private ProductRepository productRepository;
#GetMapping("/products")
public Returns products() {
return new Returns(ReturnStatus.SUCCESS.getStatus(), productRepository.findAll(), null);
}
}
It will load both of type and serial of product.
Can I only load type but not to load serial?
I don't want to add fetch=FetchType.LAZY to serial, because if next time I want to load serial but not to load type, it will be terrible.
Check the Projection interface
Create a interface ProductProjection
interface ProductProjection {
String getName();
String getType();
}
and add a method in you Repository
List<ProductProjection> findAllProjection()
That's the whole point of fetch=FetchType.LAZY. It'll not load any of your types/fields until you ask for them explicitly.
Take a look at this question: Link
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
}
The jhipster doesn't support create many to many relationships with extra fields.
What is the best way to create many to many association with extra columns in jhispter? Should i create a two one-to-many relationship with extra fields?
Using JHipster Domain Language (JDL), a #ManytoMany holding extra properties (columns) can be easily achieved using an association entity and two ManyToOne relationships. See below:
entity Foo{
...
}
entity Bar{
...
}
entity FooBarAssociation{
extraProperty1 String
extraProperty2 String
...
}
relationship ManyToOne {
FooBarAssociation{foo} to Foo{bars}
FooBarAssociation{bar} to Bar{foos}
}
You will have to do it manually.
this post describes how: https://hellokoding.com/jpa-many-to-many-extra-columns-relationship-mapping-example-with-spring-boot-maven-and-mysql/
In general, as #Antares42 said, you should create an entity for the Many-To-Many table like so:
first entity:
#Entity
public class Book{
private int id;
private String name;
private Set<BookPublisher> bookPublishers;
public Book() {
}
public Book(String name) {
this.name = name;
bookPublishers = new HashSet<>();
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<BookPublisher> getBookPublishers() {
return bookPublishers;
}
public void setBookPublishers(Set<BookPublisher> bookPublishers) {
this.bookPublishers = bookPublishers;
}
}
secound entity:
#Entity
public class Publisher {
private int id;
private String name;
private Set<BookPublisher> bookPublishers;
public Publisher(){
}
public Publisher(String name){
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "publisher")
public Set<BookPublisher> getBookPublishers() {
return bookPublishers;
}
public void setBookPublishers(Set<BookPublisher> bookPublishers) {
this.bookPublishers = bookPublishers;
}
}
Join table entity:
#Entity
#Table(name = "book_publisher")
public class BookPublisher implements Serializable{
private Book book;
private Publisher publisher;
private Date publishedDate;
#Id
#ManyToOne
#JoinColumn(name = "book_id")
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
#Id
#ManyToOne
#JoinColumn(name = "publisher_id")
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
#Column(name = "published_date")
public Date getPublishedDate() {
return publishedDate;
}
public void setPublishedDate(Date publishedDate) {
this.publishedDate = publishedDate;
}
}
This entity describes the relationship between Book and Publisher and the extra field is published_date
Let's say you have entities like Movie, Rater and needs a join table Ratings. You can write a JDL script like the following:
entity Movie { title String}
entity Rater { name String}
entity Rating { value Integer} //the extra field
relationship ManyToMany {
Rating{rater(name)} to Rater,
Rating{movie(title)} to Movie
}
save it in file.jdl in the project folder, open cmd type
jhipster import-jdl file.jdl
and you have everything
I have model. there is this part:
model was mapped by jpa annotations.Everywhere I use fetchType = EAGER. If I load vacancy from database, I have 2 duplicates status_for_vacancy objects.
I use property hbm2ddl.auto = update.
If I make new schema of database and fill data, I haven't duplicates status_for_vacancy objects.
It really?
code:
vacancy:
#Entity
#Table(name = "vacancy")
#XmlRootElement(name="vacancy")
public class Vacancy {
private List<VacancyStatus> statusList = new LinkedList<VacancyStatus>();
#OneToMany(mappedBy = "vacancy", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<VacancyStatus> getStatusList() {
return statusList;
}
public void setStatusList(List<VacancyStatus> statusList) {
this.statusList = statusList;
}
}
status_for_vacancy:
#Entity
#Table(name = "status_for_vacancy")
public class StatusForVacancy extends AbstractStatus {
public StatusForVacancy() {
super();
}
public StatusForVacancy(Integer id, String name) {
super(id, name);
}
}
#MappedSuperclass
#XmlRootElement
public abstract class AbstractStatus {
private Integer id;
private String name;
public AbstractStatus() {
super();
}
public AbstractStatus(String name) {
super();
this.name = name;
}
public AbstractStatus(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name ="id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
#NotEmpty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
vacancy_status:
#Entity
#Table(name = "vacancy_status")
public class VacancyStatus extends AbstractHistoryStatus {
private Vacancy vacancy;
private StatusForVacancy status;
public VacancyStatus() {
super();
}
public VacancyStatus(Integer id, User author, Date date,
Vacancy vacancy, StatusForVacancy status) {
super(id, author, date);
this.vacancy = vacancy;
this.status = status;
}
#ManyToOne
#JoinColumn(name = "vacancy_id")
public Vacancy getVacancy() {
return vacancy;
}
public void setVacancy(Vacancy vacancy) {
this.vacancy = vacancy;
}
#ManyToOne
#JoinColumn(name = "status_id")
public StatusForVacancy getStatus() {
return status;
}
public void setStatus(StatusForVacancy status) {
this.status = status;
}
}
#MappedSuperclass
public abstract class AbstractHistoryStatus {
private Integer id;
private User author;
private Date date;
public AbstractHistoryStatus() {
}
public AbstractHistoryStatus(Integer id, User author, Date date) {
super();
this.id = id;
this.author = author;
this.date = date;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
#Column(name="creation_date")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
It is all mapping code for these entities.
in debugger:
both id==500 ==> hibernate understand, that it is same objects.
I try add all data from old database to new database - I get old error(
I fix cause of appearance of this problem. It appearances if I add record to note table:
I highly recommend you write equals() and hashCode() methods. The standard equals()/hashCode() implement referential equality (do 2 objects reference the same memory location). So if hibernate has 2 of the 'same' object in memory, but they don't reference the same memory location then you will see the object show up twice. But if you implement equals() based on primary key being equal, then even if there are two copies of the same object in memory, Hibernate won't give you duplicates.
See the JPA spec:
2.4 Primary Keys and Entity Identity
Every entity must have a primary key. ... The value of its primary key
uniquely identifies an entity instance within a persistence context
and to EntityManager operations
Also see this SO post.