Spring Data: Fetching values from Entity with #ElementCollection - jpa

My Entity class is:
#Entity
#Data
#Table(uniqueConstraints = { #UniqueConstraint(columnNames = {"username" }, name = "uq_username") } })
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
private String firstName;
private String lastName;
#ElementCollection(targetClass = Role.class,fetch = FetchType.LAZY)
#JoinTable(name = "User_Roles", joinColumns={#JoinColumn(name = "user_id")})
#Enumerated(EnumType.STRING)
private List<Role> roles;
}
Role class is an enumeration:
public enum Role {
ADMIN,
OFFICE_USER,
SUPERVISOR,
MANAGER,
DATA_ENTRY
}
Property spring.jpa.hibernate.ddl-auto=create and the tables user and user_roles (fields: user_id, roles) are created and data inserted successfully. However, querying for data for users with a role is not working. My repository is:
#Component
public interface UserRepository extends CrudRepository<User, Integer>, JpaRepository<User, Integer> {
#Query(value = "select usr from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%"
+ " AND usr.roles=:roles")
public List<User> fetchUsersByNameAndRole(String username, Collection<String> roles);
}
On passing the following request I am getting error
http://localhost:8080/api/users/search/fetchUsernamesByNameAndRole?username=Thom&roles=SUPERVISOR
[org.springframework.data.rest.webmvc.ResourceNotFoundException: EntityRepresentationModel not found!]
If I change Repository method (changing username parameter to String from Collection) to
#Query(value = "select usr from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%"
+ " AND usr.roles=:roles")
public List<User> fetchUsersByNameAndRole(String username, String roles);
The error becomes:
[org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [SUPERVISOR] did not match expected type [java.util.Collection (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [SUPERVISOR] did not match expected type [java.util.Collection (n/a)]]
Question 1: How can I fetch all users with a name and a particular role (or set of roles) in this scenario?
The code is working if I remove the roles clause, like
#Query(value = "select usr from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%")
public List<User> fetchUsersByName(String username);
I need the user names only. So I changed code to:
#Query(value = "select usr.firstName from User usr where usr.firstName LIKE :username% OR usr.lastName LIKE :username%")
public List<String> fetchUsersByName(String username);
But gets the error:
org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.String
Changing to native query also gives same error:
#Query(value ="select first_name from user where first_name like ?1%", nativeQuery = true)
Question 2: Why mapping to List is not working in Spring data? I remember it was working with normal Spring JPA

Related

Which return value should I use for the one-to-one table when using spring boot jpa?

We are working on a club management project.
There is a club president on the club table and the user ID is received as a foreign key.
I want to join two tables while using jpa and get the results
If you specify the mapped club table as a type, an error appears (image 1)
If you interface the resulting field, only null values are returned. (Image 2)
How shall I do it?
(Image1)
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Object[]] to type [#org.springframework.data.jpa.repository.Query com.wodongso.wodongso.entity.Society] for value '{호남대학교, 왕유저13, 두 발의 자유1, 스포츠, 두 바퀴만 있다면 지원가능!!1, 허벅지 터지도록 활동합니다1, true}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [#org.springframework.data.jpa.repository.Query com.wodongso.wodongso.entity.Society]
(Image2)
society entity
user entity
#Entity
#Data
public class User {
#Id
private String id;
private String name;
private String nickname;
private String password;
private String role;
private String contact;
#Column(name = "profile_url")
private String profileUrl;
private String region;
private String university;
private String major;
#Column(name = "class_of")
private Integer classOf;
private boolean enabled;
#Column(name = "created_at")
private Date createdAt;
}
SocietyRepository
#Repository
public interface SocietyRepository extends JpaRepository<Society, Integer> {
Page<Society> findByNameContaining(String searchKeyword, Pageable pageable);
#Query("SELECT s FROM Society s WHERE s.enabled = :isEnable")
Page<Society> findByEnabledPage(#Param("isEnable") boolean isEnable, Pageable pageable);
#Query("SELECT u.university, u.name, s.name, s.category, s.simpleDesc, s.detailDesc, s.enabled " +
"FROM Society s " +
"INNER join User u " +
"ON u.id = s.officerId " +
"WHERE u.university = :university")
List<Society> findAllByUniversity(#Param("university") String university);
}
Create a class which contains all the fields and add an all args constructor and than use the query:
#Query("SELECT new SocietyWithUser(u.university, u.name, s.name, s.category, s.simpleDesc, s.detailDesc, s.enabled) " +
"FROM Society s " +
"INNER join User u " +
"ON u.id = s.officerId " +
"WHERE u.university = :university")
List<SocietyWithUser> findAllByUniversity(#Param("university") String university);

JPARepository - delete using date comparison with derived query

I'm trying to use JPARepository in Spring Boot to delete records that are less than a certain date, for for a given userid
Should be something like this Delete * from [table] where expiration_date < [date] and userid = [userid]
I thought I should be able to use one of the automatically generated methods
int deleteByExpiryDateBeforeAndUser(Date date, User user);
But this is generating a Select and not a Delete. What am I doing wrong?
Update
Entity class
#Getter
#Setter
#ToString
#Entity(name = "refresh_token")
public class RefreshToken {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
#Column(nullable = false, unique = true)
private String token;
#Column(nullable = false)
private Date expiryDate;
public RefreshToken() {
}
}
Repository class
#Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByToken(String token);
#Modifying
void deleteByUserIdAndExpiryDateBefore(Long userId, Date expiryDate);
int deleteByUser(User user);
}
Here's how I'm calling it
#Transactional
public void deleteExpiredTokens(User user) {
refreshTokenRepository.deleteByUserIdAndExpiryDateBefore(user.getId(), new Date());
}
You see a select statement because Spring Data first loads entities by condition.
Then once entities became 'managed' Spring Data issues a delete query for each entity that was found.
If you want to avoid redundant SQL query - you have to consider #Query annotation.
Then your code will look like this:
#Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
// ...
#Query(value = "DELETE FROM refresh_token WHERE user_id =:userId AND expiry_date < :expiryDate", nativeQuery = true)
#Modifying
void deleteByUserIdAndExpiryDateBefore(Long userId, Date expiryDate);
//...
}

How to return a count column not exists in table by JPA

I want find a way to get extra column that count my records and return it in 1 mapping entity with extra filed.
I tried #transient on field but it will not return value when query.
Then I remove #transient but get an exception when save.
Also I tried #Formula but received null pointer exception.
Here's my repository code:
#Query(value = "select id,account,session_id,create_time,count from query_history a join " +
"(select session_id sessionId,max(create_time) createTime,count(*) count from query_history group by session_id) b " +
"on a.session_id = b.sessionId and a.create_time = b.createTime where account = ?1 order by create_time desc",
countQuery = "select count(distinct(session_id)) from query_history where account = ?1",
nativeQuery = true)
Page<QueryHistory> findByNtAndGroupBySessionAndAction(String account, Pageable pageable);
entity code:
#Entity
#Table(name = "query_history")
#Data
public class QueryHistory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String account;
#Column
private Long sessionId;
#Column
private long createTime;
#Transient
private Integer count;
}
Sorry about my English and thanks a lot for any advice.
I solved the problem by projections spring-data-projections, in fact I tried this before but in my sql:
select id,account,session_id,create_time,count
which should be:
select id,account,session_id sessionId,create_time createTime,count
PS:
projection interface:
public interface QueryHistoryWithCountProjection {
Long getId();
String getAccount();
Long getSessionId();
long getCreateTime();
Integer getCount();
}

Custom query in spring boot JPA using postgres db

I am connecting my spring boot application to postgres db, i am able insert values using JPA. Now i want to retrieve data from the table. I would like to use the #Query from JPA to select the column in the table. while i run the application i get sql error. Here is the repository code
#Query(value = "SELECT user.firstname AS firstName, user.lastName AS lastName FROM user_details user WHERE emailid LIKE ?1", nativeQuery = true)
EmailOnly findUserByEmail(String emailId);
public static interface EmailOnly {
String getFirstName();
String getLastName();
}
Entity class
#Getter
#Setter
#Entity
#Table(name = "USER_DETAILS")
public class UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private int userId;
private String emailId;
private String firstName;
private String lastName;
}
I get error as
SQL Error: 0, SQLState: 42601 2020-03-23 17:04:05.128 ERROR 27508 ---
[nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR:
syntax error at or near "."
All the example in jpa shows to select data using aliases, but its now working for me. If i try to remove all the aliases from the query i am getting the projection values as null.

Delete entity from many to many in jpa

I have an Entity
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "friends")
private Set<User> friends;
}
Currently I remove a friend by getting first all the friends from database which is not efficient/optimal
#Override
#Transactional
public void deleteFriend(String username, String friend) {
User user = getUser(username);
User other = getUser(friend);
user.getFriends().remove(other);
other.getFriends().remove(user);
}
Since it's not efficient I would like to use this query
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
void removeByUsernameAndFriendsUsername(String username, String otherUsername);
}
And when I try to delete a friend with this query I get an exception
Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FK8KCUM44FVPUPYW6F5BACCX25C: PUBLIC.COMMENT FOREIGN KEY(USER_ID) REFERENCES PUBLIC.USER(ID) (3)"; SQL statement:
delete from user where id=? and version=? [23503-196]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:425)
at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:442)
at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:317)
at org.h2.table.Table.fireConstraints(Table.java:980)
at org.h2.table.Table.fireAfterRow(Table.java:998)
at org.h2.command.dml.Delete.update(Delete.java:101)
at org.h2.command.CommandContainer.update(CommandContainer.java:101)
at org.h2.command.Command.executeUpdate(Command.java:260)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:164)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:150)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
... 68 more
Do you know if it's possible to delete it with #NamedQuery or #Query where I don't have to get all the friends first? Something like the query below.
#Query(value = "delete from User U join U.friends friends where U.username = ?1 and friends.username = ?2")
Here's the comment entity
#Entity
#Table(name="comment")
public class Comment {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#NotNull
#Size(min=1)
#Column(name="text", nullable=false)
private String text;
#NotNull
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
Try:
#Modifying
#Query("delete from User U join U.friends friends where U.username = ?1 and friends.username = ?2)
....