My picture table with two OneToOne relations to the User table:
Picture entity:
#Entity
#Table(name = "pictures")
public class Picture implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
public int getId() {
return id;
}
#OneToOne
#JoinColumn(name = "customer_id", nullable = true)
private User customer;
public User getCustomer() {
return customer;
}
public void setCustomer(User customer) {
this.customer = customer;
}
#OneToOne
#JoinColumn(name = "photographer_id", nullable = false)
private User photographer;
public User getPhotographer() {
return photographer;
}
public void setPhotographer(User photographer) {
this.photographer = photographer;
}
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
private BigDecimal price;
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Picture(String title,
String url,
BigDecimal price)
{
this.title = title;
this.url = url;
this.price = price;
}
public Picture() {
// Empty constructor
}
}
PictureDaoImpl:
#Override
public void insert(Picture object) {
sessionFactory.getCurrentSession().save(object);
}
How can I update the two foreign keys customer_id and photographer_id?
In the entity the two foreign keys are OneToOne relations to User
The error I get is Column 'photographer_id' cannot be null
You made an extremely wrong DB design.
Between DB entities you can't make two relations, between tables\entities
can be only and only single relation.
According to your design you need to relation of #OneToMany relation between Picture and User, for every single/one picture you have many users, even if da facto you'll eventually have only two users, because two are still not one/single, so it's being considered as Many.
I suggest that first do your redesign DB.
Related
I am trying to persist an object to the DB, where the id of the object is auto generated. The issue is that for some reason the ID remains null(I also tried hardcoding a id in the constructor, but it is still seen as null). I tested inserting an object to the DB running a sql query and I can see the entry in the table.The model class is:`
package com.dealFinder.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#ToString
#Entity
#Table(name = "deals")
public class DealModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank(message = "Title is mandatory")
private String title;
#NotBlank(message = "Url is mandatory")
private String url;
private float price;
public DealModel(String title, String url, float price){
// this.id = 1;
this.title=title;
this.url = url;
this.price= price;
}
public DealModel(){}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "title", nullable = false)
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Column(name = "url", nullable = false)
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
#Column(name = "price", nullable = false)
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
I create the object with `
model = new DealModel(title, productUrl, price);
where title, productUrl and price are not null, and I persist it to the DB with
dealsRepository.save(dealModel);
where the dealRepository class is
#Repository
public interface DealsRepository extends JpaRepository<DealModel, Integer> {
}
` Not sure what wrong change I made recently, as it was working fine previously.
I am trying to persist the delModel object to the DB. Running a manual query to insert a dealModel entry to the table works fine
I have class Product with members like Name, Description... which are private, and I have public getter and setter methods. I tried to make Spring Boot MVC REST controller with POST method but it seams that what I am sending is not bind to model. This is JSON I'm sending to controller:
{
"Name": "proizvod7",
"Description": "neki opis2",
"CategoryId":1,
"Price":"15"
}
This is my code:
#RequestMapping(value = {"","/"},method = RequestMethod.POST,consumes = "application/json",produces="application/json")
public ResponseEntity PostProduct(#RequestBody #Valid Product p, BindingResult result){
if (!result.hasErrors()) {
return new ResponseEntity<>(service.insert(p), HttpStatus.CREATED);
} else
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
I figured that out when I created new class ProductDto which has public members Name, Description...
This is working version:
#RequestMapping(value = {"","/"},method = RequestMethod.POST,consumes = "application/json",produces="application/json")
public ResponseEntity PostProduct(#RequestBody #Valid ProductDto productDto, BindingResult result) {
if (!result.hasErrors()) {
Product p = new Product(productDto.Name, productDto.Price, productDto.Description, productDto.CategoryId);
return new ResponseEntity<>(service.insert(p), HttpStatus.CREATED);
} else {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
However project is simple enough so I don't want to introduce DTO classes, but I would like to have private members inside my initial Product class with public getters and setters. Is it possible?
EDIT:
Here is Product class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "products")
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
public Product(#NotNull String name, BigDecimal price, String description, Long category_id) {
Name = name;
Price = price;
Description = description;
Category_id = category_id;
}
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public BigDecimal getPrice() {
return Price;
}
public void setPrice(BigDecimal price) {
Price = price;
}
public String getDescription() {
return Description;
}
public void setDescription(String description) {
Description = description;
}
#NotNull
private String Name;
private BigDecimal Price;
private String Description;
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public Long getCategory_id() {
return Category_id;
}
public void setCategory_id(Long category_id) {
Category_id = category_id;
}
#Column(name = "category_id",nullable = true)
private Long Category_id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="category_id",updatable = false,insertable = false)
private Category category;
}
It seems your field names are breaking the deserializing logic. I do not know why are you trying to use those uppercase field names, but the problem is that when there is a setter for Name field, setName(), Jackson thinks this maps to a field name, not Name, hence the issue.
You should use #JsonProperty()
#JsonProperty("Name")
private String Name;
#JsonProperty("Price")
private BigDecimal Price;
#JsonProperty("Description")
private String Description;
#JsonProperty("categoryId")
#Column(name = "category_id", nullable = true)
private Long Category_id;
Also with #Data annotation from Lombok, you do not need to write all those getters/setters, they are already generated for you.
You can use #JsonSetter at the public setters... something like this
public class MyBean {
private String name;
#JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}
You can check more examples here:
https://www.baeldung.com/jackson-annotations
#JsonSetter ref here https://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonSetter.html
I'm trying to map 2 entities (Course and Student), I have 2 Java classes and 2 MySQL tables, having a ManyToMany relationship. I created the junction table and java class Enrolment (as I want extra information such as the date of enrolment of a student to a course).
I'm trying to insert data using hibernate in this Enrolments table in the MySQL but I keep getting errors. Here are my POJO classes:
Course class:
#Entity
#Table(name = "course")
public class Course {
private int id;
#Column(name = "chapter_id")
private int chapterId;;
#Column(name = "name")
private String title;
#Column(name = "teacher_user_id")
private int teacherId;
#OneToMany(targetEntity=Enrolment.class, mappedBy="course", fetch=FetchType.LAZY)
// #JoinTable(name = "enrolment",
// joinColumns = #JoinColumn(name = "course_id"),
// inverseJoinColumns = #JoinColumn(name = "student_user_id"))
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Course(){}
public Course(int id, int chapterId, String title, int teacherId) {
super();
this.id = id;
this.chapterId = chapterId;
this.title = title;
this.teacherId = teacherId;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getChapterId() {
return chapterId;
}
public void setChapterId(int chapterId) {
this.chapterId = chapterId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getTeacherId() {
return teacherId;
}
public void setTeacherId(int teacherId) {
this.teacherId = teacherId;
}
#OneToMany(mappedBy = "course")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
Student class (this class is inherited from User parent class, I will attach User Class down below as well. In the database there are different tables as well: User and then Student and Teacher that inherit User parent entity):
#Entity
#Table(name = "student")
#PrimaryKeyJoinColumn(name = "user_id")
#OnDelete(action = OnDeleteAction.CASCADE)
public class Student extends User {
private int grade;
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Student(){}
public Student(String fname, String lname, String email, String password, String address, String phone,
int userType, int grade, boolean isAdmin)
{
super(fname, lname, email, password, address, phone, userType, isAdmin);
this.grade=grade;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
#OneToMany(mappedBy = "student")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void addCourse(Enrolment course) {
this.enrolments.add(course);
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
User Class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstname;
private String lastname;
private String email;
private String password;
private String address;
private String phone;
#Column(name = "user_type_id")
private int userType;
#Column(name = "is_admin")
private boolean isAdmin;
public User(String fname, String lname, String email, String password, String address, String phone,
int userType, boolean isAdmin) {
//super();
this.firstname = fname;
this.lastname = lname;
this.email = email;
this.password = password;
this.address = address;
this.phone = phone;
this.userType = userType;
this.isAdmin = isAdmin;
}
public User() {}
//getters & setters
And finally this is the Enrolment class:
#Entity
#Table(name = "enrolment")
public class Enrolment {
private int id;
private Student user;
private Course course;
#Column(name = "enrolment_date")
private Date enrolmentDate;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "student_user_id")
public User getUser() {
return user;
}
public void setUser(Student user) {
this.user = user;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "course_id")
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Date getEnrolmentDate() {
return enrolmentDate;
}
public void setEnrolmentDate(Date enrolmentDate) {
this.enrolmentDate = enrolmentDate;
}
So I'm trying to read a course and a student from database and insert the information in this Enrolment table but it gives errors since trying to read a Course. Here is the DAO method:
#SuppressWarnings("deprecation")
#Transactional
public List<Course> getCoursesOfChapter(int chapterId) {
Configuration con = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Course.class);
SessionFactory sf = con.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Course where chapter_id = :chapterId");
query.setParameter("chapterId",chapterId);
// List list = query.list();
tx.commit();
return (List<Course>) query.list();
It throws the error at the session factory building:
Exception in thread "main" org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: models.Course.enrolments[models.Enrolment]
at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1255)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:808)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:733)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1696)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1664)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:84)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:474)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
at dao.CourseDAO.getCourse(CourseDAO.java:52)
at webapp.Main.main(Main.java:132)
Finally, this is my call:
CourseDAO cdao = new CourseDAO();
Course course = cdao.getCourse(1);
I've tried playing with the annotations, make them ManyToMany instead of ManyToOne. I tried to map the User class instead of Student but still didn't work. I tried to make it without the junction class of Enrolment and just generate it without having an actual class for it but still didn't work (as I had to work with 2 session.save() methods one after the other which also gave some error that I couldn't solve). Probably it's a small thing that I'm missing here but I just can't figure it out, sorry for too long code but I really need to solve it fast. If you read through here, I really thank you!
So my question is: Am I missing something here from these mappings and annotations or I should change the structure of my classes?
Boiling down a problem to the bare minimum greatly helps others help you. Here are simpler versions of your student, course and enrollment classes that can be unit tested easily. The many-to-many association between course and student is separated into two many-to-one associations from Enrollment. Note that the associations are bidirectional and that the many side is mapped by the one side. Student cascades persistence operations to Enrollment, which reflects how schools normally work: students enroll in courses, not the other way around.
Course.java
#Entity
public class Course {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(mappedBy = "course")
private List<Enrollment> enrollments;
Course() {
}
Course(String title) {
this.title = title;
this.enrollments = new ArrayList<>();
}
void add(Enrollment enrollment) {
enrollments.add(enrollment);
}
Long getId() {
return id;
}
List<Enrollment> getEnrollments() {
return enrollments;
}
}
Student.java
#Entity
public class Student {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "student", cascade = ALL, orphanRemoval = true)
private List<Enrollment> enrollments;
Student() {
}
Student(String name) {
this.name = name;
this.enrollments = new ArrayList<>();
}
void enroll(Course course) {
enrollments.add(new Enrollment(course, this));
}
}
Enrollment.java
#Entity
public class Enrollment {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Course course;
#ManyToOne
private Student student;
Enrollment() {
}
Enrollment(Course course, Student student) {
this.course = course;
this.student = student;
course.add(this);
}
}
The test case below checks that the entities are mapped and associated correctly. You can run it with Spring Boot.
SchoolTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class SchoolTest {
#Autowired
private CourseRepository courseRepository;
#Autowired
private StudentRepository studentRepository;
#Test
public void run() {
Course course = courseRepository.save(new Course("cs_101"));
int studentCount = 3;
for (int i = 1; i <= studentCount; i++) {
Student student = new Student("student_" + i);
student.enroll(course);
studentRepository.save(student);
}
// push changes to the database and clear the existing entities
// to make the subsequent operations load from the database
entityManager.flush();
entityManager.clear();
Optional<Course> savedCourse = courseRepository.findById(course.getId());
assertTrue(savedCourse.isPresent());
assertEquals(studentCount, savedCourse.get().getEnrollments().size());
}
}
As the warning said, your Enrollment is not registered in Hibernate. If you really don't need it. Please use transient annotation. read more here
I am making a small library project in Java EE. I have created 3 tables and class with authors, genres and books. Now I am trying to connect it using hibernate, but i haven't ide how confire annotations ... Please help me :)
bookTable:
| id_book | author_id | title | genre_id | description | photo |
genreTable:
| genre_id | genre |
authorTable:
| author_id | author|
It is easy structure:
bookTable.author_id - authorTable.author_id = ManyToMany
bookTable.genre_id - genreTable.genre_id = OneToOne
Below there are my pojo class:
Book
#Entity
#Table(name = "books")
public class Book implements Serializable{
private static final long serialVersionUID = -5057364006691079475L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Integer user_id;
private Author author;
private String description;
private BookGenre genre;
private String title;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getUser_id() {
return user_id;
}
public void setUser_id(Integer user_id) {
this.user_id = user_id;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public BookGenre getGenre() {
return genre;
}
public void setGenre(BookGenre genre) {
this.genre = genre;
}
}
Author
#Entity
#Table(name = "author")
public class Author implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "author_id")
private Integer author_id;
#Column(name = "author")
private String author;
public Integer getAuthor_id() {
return author_id;
}
public void setAuthor_id(Integer author_id) {
this.author_id = author_id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Genre
#Entity
#Table(name = "genre")
public class BookGenre implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "genre_id")
private Integer genreId;
#Column(name = "genre")
private String genre;
public Integer getGenreId() {
return genreId;
}
public void setGenreId(Integer genreId) {
this.genreId = genreId;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
}
Should it be a uni-directional or bi-directional association?
Have a look at this example:
https://howtoprogramwithjava.com/hibernate-manytomany-unidirectional-bidirectional/
It even uses your entity names :)
That are entitys and no pojos, but they look good so far. For them you normally don't need to take care. Best way, you autogenerate them after you connected your project with the database. Hibernate will take care for everything. What is more interesting is how your DAO looks, bcz. that is the layer communication with your database. The entity is only the representation of the database table on Java side. I guess you already connected your project with the database?
Please provide your Database Access Object (DAO) for further help. If you havent that so far here you can get help.
I define the following entities :BaseEntity , magasin and article :
#Entity(name = "magasin")
#Table(name = "magasin")
public class Magasin extends BaseEntity {
private static final long serialVersionUID = 1L;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#OneToMany(cascade=CascadeType.ALL, mappedBy="magasin")
#Valid
private Set<Article> article;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Article> getArticle() {
return article;
}
public void setArticle(Set<Article> article) {
this.article = article;
}
}
#Entity(name="article")
#Table(name="article")
public class Article extends BaseEntity {
private static final long serialVersionUID = 1L;
#ManyToOne
private Magasin magasin;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#Basic
private float price;
public Magasin getMagasin() {
return magasin;
}
public void setMagasin(Magasin magasin) {
this.magasin = magasin;
}
public String getName() {
return name;
}
public void setName(String nom) {
this.name = nom;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public boolean isNew() {
return (this.id == null);
}
}
How can create a hql query in order to retrieve all Article for a magasin selected ?
I try this
#Override
public List<Article> findArticle(Magasin magasin) {
String query = "From Article m where m.magasin.id = "+magasin.getId();
System.out.print(query);
Session session = getSessionFactory().getCurrentSession();
if((session.createQuery(query).list()!=null) && (session.createQuery(query).list().size()!=0))
return (List<Article>) session.createQuery(query).list();
else
return null;
}
But it returns nothing , always null .How can I resolve it ?
I don't know the type of your magasin id so adapt the code below.
First get the Magasin instance for the id:
Magasin mag = (Magasin)session.get(Magasin.class, id);
Next you can access the articles for the magasin mag via accessor
Set<Article> articles = mag.getArticle();
Try this:
"Select * From Article,Mgasin where Article.mgasin.id = "+magasin.getId();