I have this entity:
#Entity
#Table(name = "stats_search")
public class Search implements Serializable {
#Id
#GeneratedValue
#Column(name = "stats_search_uuid")
#Type(type="pg-uuid")
private UUID id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="city_id", nullable=false, referencedColumnName = "id")
private City city;
#Column
private Double lat;
#Column
private Double lng;
#Column
private Integer radius;
#Column(name = "search_time")
private LocalDateTime searchTime;
#Column(name = "ip_address")
private String ipAddress;
}
And this repository:
public interface StatSearchRepository extends JpaRepository<Search, UUID> {
#Query(value = "SELECT stats_search.* FROM stats_search " +
"WHERE ip_address = :ipAddress " +
"AND search_time > (NOW() - ((INTERVAL '1 seconds') * :delay)) " +
"ORDER BY search_time DESC LIMIT 1",
nativeQuery = true)
Search findIpAddress(String ipAddress, Integer delay);
}
But, when I edit the returned object (by editing one of its properties) and call save(), I get an exception because its PK is allready assigned. I think it's because my object is detached.
Related
There are two tables Books and Users. They are connected by a many-to-many relationship and have a crosstab between them with columns book_id, reader_id, start date, and end date. I need to take data from this table as User.login, Books.title, start_date and end_date. How can I get data from this table if I have the following entities:
Book Entity:
#Entity
#Table(name = "books")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "publishing_year")
private Integer publishingYear;
#Column(name = "sell_cost")
private BigDecimal sellCost;
#Column(name = "rent_cost")
private BigDecimal rentCost;
#Column(name = "amount")
private Integer amount;
//=======Relationships=======
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH,CascadeType.REMOVE})
#JoinTable(name = "rented_books",
joinColumns = #JoinColumn(name = "book_id"),
inverseJoinColumns = #JoinColumn(name = "reader_id"))
private List<User> usersRented;
}
User Entity:
#Entity
#Table(name = "users")
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "username")
private String login;
#Column(name = "password")
private String password;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Column(name = "wallet")
private Integer wallet;
#Column(name = "enabled")
private boolean enabled;
//=======Relationships=======
#ManyToMany(mappedBy = "usersRented")
private List<Book> rentedBooks;
Book Repository
#Repository
public interface BookRepository extends CrudRepository<Book,Long> {
#Query(value = "SELECT b.title,rb.start_date,rb.expiration_date FROM books b INNER JOIN rented_books rb ON rb.book_id = b.id INNER JOIN users u ON u.id = rb.reader_id WHERE u.id = ?1",nativeQuery = true)
Page<Book> findAllRentedBookByUser(Long id, Pageable pageable);
}
But this query doesn't work, throws this error:
java.sql.SQLException: Column 'id' not found.
Although in DBMS this query works fine
You are returning the Book entity so you need to select all fields for that entity. You can't return a partially filled entity, because JPA does not support partial entity population from native queries.
According to JPA specification:
3.10.16.1 Returning Managed Entities from Native Queries
When an entity is to be returned from a native query, the SQL
statement should select all of the columns that are mapped to the
entity object. This should include foreign key columns to related
entities. The results obtained when insufficient data is available are
undefined
Example of correct query:
#Repository
public interface BookRepository extends CrudRepository<Book,Long> {
#Query(value = "SELECT b.* FROM books b INNER JOIN rented_books rb ON rb.book_id = b.id INNER JOIN users u ON u.id = rb.reader_id WHERE u.id = ?1", nativeQuery = true)
Page<Book> findAllRentedBookByUser(Long id, Pageable pageable);
}
In case you want to return a custom column set you need to create an additional simple POJO class and define a mapping for it.
Example: How to fix convertion error in Nativequery in Spring-boot
1. Create a custom POJO class
import java.util.Date;
public class BookDetails {
private String title;
private Date start_date;
private Date expiration_date;
public BookDetails(String title, Date start_date, Date expiration_date) {
this.title = title;
this.start_date = start_date;
this.expiration_date = expiration_date;
}
public String getTitle() {
return this.title;
}
public Date getStart_date() {
return this.start_date;
}
public Date getExpiration_date() {
return this.expiration_date;
}
public void setTitle(String title) {
this.title = title;
}
public void setStart_date(Date start_date) {
this.start_date = start_date;
}
public void setExpiration_date(Date expiration_date) {
this.expiration_date = expiration_date;
}
}
2. Define #NamedNativeQuery and mapping for POJO class
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;
#Entity
#Table(name = "books")
#NamedNativeQuery(name ="BookDetailsByUser",
query =
" SELECT b.title, rb.start_date, rb.expiration_date " +
" FROM books b INNER JOIN rented_books rb ON rb.book_id = b.id INNER JOIN users u ON u.id = rb.reader_id " +
" WHERE u.id = ?1 ",
resultSetMapping = "BookDetailsMapping"
)
#SqlResultSetMapping(name="BookDetailsMapping",
classes = {
#ConstructorResult(targetClass = BookDetails.class,
columns = {#ColumnResult(name = "title"),
#ColumnResult(name = "start_date"),
#ColumnResult(name = "expiration_date")
})
})
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "publishing_year")
private Integer publishingYear;
#Column(name = "sell_cost")
private BigDecimal sellCost;
#Column(name = "rent_cost")
private BigDecimal rentCost;
#Column(name = "amount")
private Integer amount;
//=======Relationships=======
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH,CascadeType.REMOVE})
#JoinTable(name = "rented_books",
joinColumns = #JoinColumn(name = "book_id"),
inverseJoinColumns = #JoinColumn(name = "reader_id"))
private List<User> usersRented;
}
3. Use named query in repository
#Repository
public interface BookRepository extends CrudRepository<Book,Long> {
#Query(name = "BookDetailsByUser", nativeQuery = true)
Page<BookDetails> findBookDetailsByUser(Long id, Pageable pageable);
}
I'm trying to achieve something like sql command below by using HQL and JPA.
Instead of "SELECT user_id..." I need SELECT OBJECT(o).
SELECT user_id FROM posix_user o INNER JOIN postgre_user n ON n.id=o.user_id WHERE n.name='USERNAME2'
I have some problems with this part of the code in JPA DAO:
public List<PosixUserEntity> listPosixUsers(final String uid_number) {
final StringBuilder queryString = new StringBuilder("SELECT OBJECT(o) FROM ");
queryString.append(this.entityClass.getSimpleName());
queryString.append(" o JOIN com.services.dao.user.jpa.UserEntity n ON (n.id=o.user_id) WHERE n.name LIKE :uid_number");
final Query findByNameQuery = entityManager.createQuery(queryString.toString()).setParameter("uid_number", uid_number);
return findByNameQuery.getResultList();
}
JOIN ON is not allowet here and I did not know how to replace it.
Also how can I replace com.services.dao.user.jpa.UserEntity by something cleaner.
There is my Entites, they may need to be improved:
#Entity
#Table(name = "posix_user")
public class PosixUserEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
//#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String user_id;
#Column(name = "uid_number")
private String uid_number;
#Column(name = "home_directory")
private String home_directory;
#Column(name = "login_shell")
private String login_shell;
#Column(name = "group_id")
private String group_id;
//getters,setters....
#Entity
#Table(name = "postgre_user")
#SQLDelete(sql = "update postgre_user set status = 'removed' where id = ?")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", unique = true, nullable = false)
private String name;
#Column(name = "password")
private String password;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private UserStatus status;
#Column(name = "firstname")
private String firstName;
#Column(name = "lastname")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "usertype")
private String userType;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserRoleTargetGroupEntity> userRoleTargetGroupEntity;
#Column(name = "last_login")
private String lastLogin;
#Column(name = "previous_login")
private String previousLogin;
#JsonIgnore
#Column(name = "change_password_flag")
private Boolean userPasswordResetFlag;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private PosixUserEntity posixUserEntity;
You may also need to know that FOREIGN KEY (user_id) REFERENCES postgre_user (id) - it should look like that
Can you know how can I modify my SELECT?
I've tested a simplified version of your classes
#Entity
#Table(name = "posix_user")
public class PosixUserEntity {
#Id
#Column(name = "user_id")
private Long user_id;
// getter + setter
}
#Entity
#Table(name = "postgre_user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private PosixUser posixUserEntity;
// getter + setter
}
And this JPQL query works as expected
String jpql = "SELECT p "
+ "FROM UserEntity n JOIN n.posixUserEntity p "
+ "WHERE n.name LIKE :uid_number)";
JOIN is allowed because you have mapped the relationship in UserEntity.
and you don't need to specify the complete name of your entity class.
Check if it has been included when you define your persistence unit.
Hope this helps.
I want to get some fields and then set it to my Test.entity. My SQL query:
query = "SELECT t.id as tId, t.test_name, t.duration, q.id as qId, " +
"q.question as question, q.is_multichoice as is_multichoice, " +
"q.is_open as is_open, a.id as aId, a.answer_text as answer_text FROM result r " +
"JOIN test t ON r.test_id = t.id " +
"JOIN user u ON r.user_id = u.id " +
"JOIN question q ON t.id = q.test_id JOIN answer a ON q.id = a.question_id " +
"WHERE t.id = :testId AND u.id = :userId AND r.permission = :permissionId " +
"AND q.archived = false AND a.archived = false", resultClass = com.bionic.entities.Test.class)
Test Entity:
public class Test {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "duration", nullable = false)
private int duration;
#Column(name = "test_name", nullable = false, unique = true)
private String testName;
#Column(name = "archived", nullable = false)
private boolean archived;
#OneToMany(mappedBy = "test", fetch = FetchType.EAGER)
private Set<Question> questions;
#ManyToMany(mappedBy = "tests")
private Set<User> users;
Question Entity:
public class Question {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "is_multichoice", nullable = false)
private boolean isMultichoice;
#Column(name = "is_open", nullable = false)
private boolean isOpen;
#Column(name = "picture")
private String picture;
#Column(name = "question")
private String question;
#ManyToOne
#JoinColumn(name = "test_id", nullable = false)
private Test test;
#Column(name = "archived", nullable = false)
private boolean isArchived;
#OneToMany(mappedBy = "question", fetch = FetchType.EAGER)
private Set<Answer> answers;
Answer Entity:
public class Answer {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "answer_text", nullable = false)
private String answerText;
#Column(name = "mark", nullable = false)
private int mark;
#ManyToOne
#JoinColumn(name = "question_id")
private Question question;
#Column(name = "picture")
private String picture;
#Column(name = "archived", nullable = false)
private boolean isArchived;
However, after executing this query i am getting exeption :
Caused by: java.sql.SQLException: Column 'id' not found.
DAO.class:
public Test getCurrentTest(long id, long testId, long permissionId) {
Query query = em.createNamedQuery("getCurrentTestById");
query.setParameter("userId", id);
query.setParameter("testId", testId);
query.setParameter("permissionId", permissionId);
return (Test) query.getSingleResult();
}
What am i doing wrong?
Your query doesn't return a field named id. It has fields named aId, qId, and tId.
You need to use the correct column names in your entities. For example, in your Test entity, you declared a column named id. Except your query doesn't return a column named id, it returns a column named tId. See below for an example of what needs to be changed.
public class Test {
#tId
#Column(name = "tId")
#GeneratedValue(strategy = GenerationType.AUTO)
private long tId;
....
i m trying to run a HQL query which is giving me error saying:
org.hibernate.QueryException: could not resolve property:
UserType of: EntityPack.UserTypeMenu
[from EntityPack.UserTypeMenu as utm ,EntityPack.UserType as ut
where utm.UserType.userTypeId=ut.userTypeId and ut.userTypeDesc like ' %ad%' ]
this is the function where i write query :
public ObservableList PostTableusertypemenu(String search, int q) {
ObservableList data;
data = FXCollections.observableArrayList();
List ll=null;
ll = pop.UrviewTable("from UserTypeMenu as utm ,UserType as ut "+
"where utm.UserType.userTypeId=ut.userTypeId"+
" and ut.userTypeDesc like ' %"+ search +"%' ");
Iterator ite = ll.iterator();
while (ite.hasNext()) {
UserTypeMenu obj = (UserTypeMenu) ite.next();
data.add(obj);
}
return data;
}
my UserType entity class :
#Entity
#Table(name = "user_type")
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "User_Type_Id")
private Integer userTypeId;
#Basic(optional = false)
#Column(name = "User_Type")
private String userType;
#Basic(optional = false)
#Column(name = "User_Type_Desc")
private String userTypeDesc;
#Basic(optional = false)
#Column(name = "Status")
private String status;
#Basic(optional = false)
#Column(name = "Markers")
private String markers;
}
UserTypeMenu Entity class :
#Entity
#Table(name = "user_type_menu")
#NamedQueries({
#NamedQuery(name = "UserTypeMenu.findAll", query = "SELECT u FROM UserTypeMenu u")})
public class UserTypeMenu implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "User_Type_Menu_Id")
private Integer userTypeMenuId;
#Basic(optional = false)
#Column(name = "Status")
private String status;
#Basic(optional = false)
#Column(name = "Markers")
private String markers;
#ManyToOne(optional = false)
private UserType userType;
#ManyToOne(optional = false)
private UserMenuMaster userMenuMaster;
#ManyToOne(optional = false)
private UserMenuBar userMenuBar;
}
what i want is to get data from UsertypeMenu based on Usertype description.
please help me..
thank you. :)
You don't write joins in HQL like in SQL, you use . notation to navigate through object graph. Try this
"from UserTypeMenu as utm where utm.userType.userTypeDesc like ' %"+ search +"%' "
Actually, you can use joins but directly on associations. Here is the same query but using join syntax
"from UserTypeMenu as utm join utm.userType ut where ut.userTypeDesc like ' %"+ search +"%' "
The benefit of using joins here is that you can specify, for example, left join if the relation is not mandatory and you don't want to lose any results because of inner join which is implicitly used when you use ..
I have the main entity class with the below fields where there is a field finid which references patient entity class. :-
public class Patientrel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "patientrelid")
private Long patientrelid;
#Column(name = "entrydate")
#Temporal(TemporalType.TIMESTAMP)
private Date entrydate;
#JoinColumn(name = "finid", referencedColumnName = "fin_id")
#ManyToOne
private Patient finid;
Entity class of Patient :-
public class Patient implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "fin_id")
private Integer finId;
#Size(max = 100)
#Column(name = "patient_name")
private String patientName;
#OneToMany(mappedBy = "finid")
private Collection<Patientrel> patientrelCollection;
Now i need to search patientrel matching a given finid. Can anyone please share the approach for that?
Now i need to search patientrel matching a given finid.
First option is to get the Patient by finId, and then retrieve the Patientrel collection thru .getPatientrelCollection() getter method:
EntityManager em = ...
Integer findId = 1;
Patient patient = em.find(Patient.class, findId );
Collection<Patientrel> patientrelCollection = patient.getPatientrelCollection();
Second option is to to use a JQL query which joins Patient and Patientrel entities to "search" for patientrels matching a given finid:
EntityManager em = ...
Integer findId = 1;
Query q = entityManager.createQuery(
"SELECT prel FROM " + Patient.class.getName() + "p " +
"join p.patientrelCollection prel " +
"WHERE p.finId = :id");
q.setParameter("id", findId);
List<Patientrel> patientrel = (List<Patientrel>)q.getResultList();