Supported keywords inside method names in Spring JPA - java

How do I write the method name for the below query:
#Query("SELECT r.skill, r.rating, count(r) FROM AssociateRating r "
+ "where updatedTime = ( select max(updatedTime) from AssociateRating a "
+ "where r.associate = a.associate ) GROUP BY r.skill, r.rating")
List<Object[]> findCountMostRecent();
Please find below the model:
#Entity
#Table(name = "associate_ratings")
public class AssociateRating {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String associate;
#ManyToOne
#JoinColumn(name = "skill_id")
private Skill skill;
private int rating;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_time")
private Date updatedTime;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAssociate() {
return associate;
}
public void setAssociate(String associate) {
this.associate = associate;
}
public Skill getSkill() {
return skill;
}
public void setSkill(Skill skill) {
this.skill = skill;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
public Date getUpdatedTime() {
return updatedTime;
}
public void setUpdatedTime(Date updatedTime) {
this.updatedTime = updatedTime;
}
}
/**
* Skill Model
*/
#Entity
#Table(name = "skills")
public class Skill {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
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;
}
}
I am getting exception when trying to extract records from associate_ratings
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'updatedTime' in 'where clause'
Could you please advise what is wrong here?
Could you also please let me know how do we name Spring JPA findXX methods in such cases?

Indeed there is no 'updatedTime' column in your table.
The column name is 'updated_time', as shown in your #Entity updatedTime variable.
Now in case you want to create method name using JPA keywords I guess it is not possible in your query for several reasons:
You can't select specific columns (r.skill, r.rating, count(r)), but you should select them all.
You can't have nested queries.
There is no GroupBy keyword
You can find all the JPA supported keywords here: 2.3 Query methods
So in your case, you should go for the #Query approach.

Related

How to save with default value using Spring JPA?

Recently I switched into Spring Data JPA and I am wondering how is it possible to save a new object into the database with some default values.
In my example I have to save a book into my database, but, in the owner column, I need to put the value 0.
So, this how I did that with JDBC, and it works amazingly well.
public void save(Book book){
jdbcTemplate.update("INSERT INTO book(name,author,yearOfProduction,owner) VALUES (?, ?, ?, 0)",
book.getName(),book.getAuthor(),book.getYearOfProduction());
}
Now I want to do the same with Spring Data JPA. Here is my save function:
BookService
#Transactional
public void save(Book book)
{
bookRepository.save(book);
}
I have two objects: Person and Book. The relationships are: one person can have many books and one book has one owner. Here are my Person and Book classes:
Book
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id",nullable = false)
private int id;
#Column(name="name")
#NotEmpty(message = "Book name can't bee empty")
private String name;
#Column(name="author")
#NotEmpty(message = "Author name can't bee empty")
private String author;
#Column(name="yearOfProduction")
#NotNull(message = "Year of production can't bee empty")
#Min(value = 0,message = "year must be more than 1900")
private int yearOfProduction;
#ManyToOne
#JoinColumn(name = "owner_id", referencedColumnName = "id")
private Person owner;
public Book(String name, String author, int yearOfProduction) {
this.name = name;
this.author = author;
this.yearOfProduction = yearOfProduction;
}
public Book(){
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
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;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getYearOfProduction() {
return yearOfProduction;
}
public void setYearOfProduction(int yearOfProduction) {
this.yearOfProduction = yearOfProduction;
}
}
Person|
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
#NotEmpty(message = "Please enter the name")
#Size(min = 1, max = 30, message = "Length must be 2-30 symbols")
//make regex with ФИО;
private String name;
#Column(name = "ageOfBirth")
#Min(value = 0, message = "age of birth must be more than 0")
private int ageOfBirth;
#OneToMany(mappedBy = "owner")
private List<Book> books;
public Person() {
}
public Person(String name, int ageOfBirth) {
this.name = name;
this.ageOfBirth = ageOfBirth;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
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;
}
public int getAgeOfBirth() {
return ageOfBirth;
}
public void setAgeOfBirth(int ageOfBirth) {
this.ageOfBirth = ageOfBirth;
}
}
I guess this is impossible to make with Spring Data JPA? So, I made it with by adding JdbcTemplate and I definitely think it is a hard-coding approach to use Spring DATA and JdbcTemplate together.
It's also unable to make with database. Down below i am using the default definition of postgres and still get null's when create a new book.
https://www.baeldung.com/jpa-default-column-values
create table book(
id int primary key generated by default as identity,
name varchar(250) not null,
author varchar(250) not null,
yearOfProduction int not null,
owner_id int default 0 references person(id)
)
#egeorge answered my question. It is impossible to input 0 into owner table.
Since 0 has a special value, and is not a real person record, there should not be any value in that field. null is the appropriate value for a join column that is not joined to an actual record. You will need to change the logic that interprets 0 as "free" to check for null instead. (I am surprised your database let you do that to begin with. Foreign key constraints will normally reject a value that is not present in the referred table.)

Unknown column in on clause Java Hibernate

I'm trying to execute an HQL for a many to many mapping in hibernate and getting a weird issue:
Caused by: java.sql.SQLSyntaxErrorException: Unknown columnallowedage1_.allowedAgencies_organization_id' in 'on clause'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
Basically, I have two tables, Reservation_Reason_Type and Agency. I need to do a many to many before them. I believe I'm doing everything correctly, but when I run the code, its giving me this unknown column issue. Not sure why it is doing allowedage1_allowedAgencies_organization_id. It is including the variable name in generated HQL as well.
Below are the mapped classes:
Agency.java
#Entity
#Table(name = "AGENCY")
public class Agency {
#Id
#Column(name="organization_id")
private int id;
#Column(name="name")
private String name;
#Column(name="acronym")
private String acronym;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "allowedAgencies")
private Collection<ReservationReasonType> allowedReservations = new ArrayList<ReservationReasonType>();
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;
}
public String getAcronym() {
return acronym;
}
public void setAcronym(String acronym) {
this.acronym = acronym;
}
public Collection<ReservationReasonType> getAllowedReservations() {
return allowedReservations;
}
public void setAllowedReservations(Collection<ReservationReasonType> allowedReservations) {
this.allowedReservations = allowedReservations;
}
}
ReservationReasonType.java
#Entity
#Table(name = "RESERVATION_REASON_TYPE")
public class ReservationReasonType {
#Id
#Column(name = "reservation_reason_type_id")
private int id;
#Column(name = "description")
private String description;
#Column(name = "reservation_reason_type_code")
private String code;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name="RES_AGENCY", joinColumns={#JoinColumn(referencedColumnName="reservation_reason_type_id")}
, inverseJoinColumns={#JoinColumn(referencedColumnName="organization_id")})
private Collection<Agency> allowedAgencies = new ArrayList<Agency>();
public ReservationReasonType(int id, String description, String code) {
this.setId(id);
this.setDescription(description);
this.setCode(code);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Collection<Agency> getAllowedAgencies() {
return allowedAgencies;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name="RES_AGENCY", joinColumns={#JoinColumn(referencedColumnName="reservation_reason_type_id")}
, inverseJoinColumns={#JoinColumn(referencedColumnName="organization_id")})
//Set<Agency> allowedAgencies = new HashSet<Agency>();
public void setAllowedAgencies(Collection<Agency> allowedAgencies) {
this.allowedAgencies = allowedAgencies;
}
}
Below is the query that I'm using for HQL:
String query = "select rrt from ReservationReasonType as rrt join rrt.allowedAgencies age where age.acronym = :agencyAcronym)";
Below is the generated HQL which won't run because of the unknown columns:
Hibernate:
select
reservatio0_.reservation_reason_type_id as reservat1_2_,
reservatio0_.reservation_reason_type_code as reservat2_2_,
reservatio0_.description as descript3_2_
from
RESERVATION_REASON_TYPE reservatio0_
inner join
RES_AGENCY allowedage1_
on reservatio0_.reservation_reason_type_id=allowedage1_.allowedReservations_reservation_reason_type_id
inner join
AGENCY agency2_
on allowedage1_.allowedAgencies_organization_id=agency2_.organization_id
where
agency2_.acronym=?
I double checked whether I'm doing the mapping right, and I think there isn't any issue with that. One thing I'm not sure of is that I'm using Hibernate 3.0, hibernate jpa 2.0, and mysql 8.0.19. Not sure if it can be an issue with that?
Need help.
UPDATE:
For a temporary solution, I added the below two columns to the RES_AGENCY table and its working, but this is just a bandaid. Still trying to figure out what can be a proper solution:
allowedReservations_reservation_reason_type_id
allowedAgencies_organization_id

How to create a many to many relationship with extra columns in jhipster?

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

hibernate.hbm2ddl.auto=update don't work

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.

trying to run a named query

I’m doing the following:
#Entity
#SqlResultSetMapping(name="getxxxx",
entities=#EntityResult(xxxx.class,
fields = {
#FieldResult(name="x1", column = "x1"),
#FieldResult(name="x2", column = "x2")}))
#NamedNativeQuery(name=" getxxxx ",
query="select x1, x2 from yyyy",
resultSetMapping=" getxxxx ")
} )public class xxxx{
.
.
.
public xxxx() {
}
i get an error:
"Table "xxxx" cannot be resolved", the class xxxx is not a table mapped into my source,
I’m trying to query the DB and return the results into my class
is it possible?
In this situation the first thing I would try would be to remove the #Entity annotation. And then change either the class name or the native query name so that one of them is "xxxx" and one of them is "zzzz," so that I was sure I knew which thing the runtime was complaining about.
It sounds like xxxx should not be an entity bean, since JPA is not happy with returning results in non-entity beans. You must instead call createNativeQuery with just the SQL String. Then call query.getResultList() to fetch the result as a List(Object[]) and use this to fill your non entity result bean.
A few years back I wrote a blog post, that might help you perform advanced native queries with JPA.
Yes, this is possible, but a little tricky. Here's a complex example that should cover most of the bases. In this example:
You have an INVOICE object with a due date;
Each INVOICE has a many-to-one relationship with a COMPANY;
Each INVOICE also has a zero- or one-to-many relationship with a set of ITEMS
Here is the schema:
CREATE TABLE "public"."invoice" (
id SERIAL,
company_id int,
due_date date,
PRIMARY KEY(id)
);
CREATE TABLE "public"."item" (
id SERIAL,
invoice_id int,
description text,
PRIMARY KEY(id)
);
CREATE TABLE "public"."company" (
id SERIAL,
name text,
PRIMARY KEY(id)
);
The INVOICE object (incredibly convoluted example for the sake of completeness):
#Entity
#Table(name = "invoice")
#Loader(namedQuery = "loadInvoiceObject")
#NamedNativeQuery(name="loadInvoiceObject",
query="SELECT " +
"inv.id," +
"inv.due_date," +
"co.*," +
"it.*," +
"FROM invoice inv " +
"JOIN company co ON co.id = inv.company_id " +
"LEFT OUTER JOIN item it ON it.invoice_id = inv.id " +
"WHERE inv.id = :id",
resultSetMapping = "invoicemap")
#SqlResultSetMapping(name = "invoicemap",
entities = {
#EntityResult(entityClass = Invoice.class),
#EntityResult(entityClass = Company.class),
#EntityResult(entityClass = Item.class)
}
)
public class Invoice {
private Integer id;
private Date dueDate;
private Company company;
private List<Item> items = new ArrayList<Item>();
public Invoice() { /* no-args constructor */ }
#Id
#Column(name = "id", nullable = false)
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
#Column(name = "due_date")
#Temporal(TemporalType.DATE)
public Date getDueDate() { return dueDate; }
public void setDueDate(Date dueDate) { this.dueDate = dueDate; }
#ManyToOne(optional = false)
#JoinColumn(name = "company_id", nullable = false)
public Company getCompany() { return company; }
public void setCompany(Company company) { this.company = company; }
#OneToMany(mappedBy = "invoice")
public List<Item> getItems() { return items; }
public void setItems(List<Item> items) { this.items = items; }
}
The ITEM object:
#Entity
#Table(name = "item")
public class Item {
private Integer id;
private String description;
private Invoice invoice;
public Item() { /* no-args constructor */ }
#Id
#Column(name = "id", nullable = false)
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
#Column(name = "description")
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
#ManyToOne(optional = false)
#JoinColumn(name = "invoice_id", nullable = false)
public Invoice getInvoice() { return invoice; }
public void setInvoice(Invoice invoice) { this.invoice = invoice; }
}
The COMPANY object:
#Entity
#Table(name = "company")
public class Company {
private Integer id;
private String name;
private List<Invoice> invoices = new ArrayList<Invoice>();
public Company() { /* no-args constructor */ }
#Id
#Column(name = "id", nullable = false)
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
#Column(name = "name")
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#OneToMany(mappedBy = "company")
public List<Invoice> getInvoices() { return invoices; }
public void setInvoices(List<Invoice> invoices) { this.invoices = invoices; }
}

Categories

Resources