Spring Data JPA how to avoid loading a column - java

I have two entities:
#Entity
#Table(name = "locations")
#Data
#NoArgsConstructor
#RequiredArgsConstructor
public class Location {
#Id
#Column(name = "location_id", nullable = false, unique = true)
#Type(type="org.hibernate.type.UUIDCharType")
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID locationId;
#NonNull
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.ms")
#Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
#NonNull
#Type(type="org.hibernate.type.UUIDCharType")
#Column(name = "user_id", nullable = false)
private UUID userId;
#NonNull
#Column(name = "latitude", nullable = false)
private Double latitude;
#NonNull
#Column(name = "longitude", nullable = false)
private Double longitude;
}
and
#Entity
#Table(name = "users")
#Data
#NoArgsConstructor
#RequiredArgsConstructor
public class User {
#NonNull
#Id
#Type(type = "org.hibernate.type.UUIDCharType")
#Column(name = "user_id", nullable = false, unique = true)
private UUID userId;
#NonNull
#Column(name = "email", length = 100, nullable = false, unique = true)
private String email;
#NonNull
#Column(name = "first_name", nullable = false)
private String firstName;
#NonNull
#Column(name = "second_name", nullable = false)
private String secondName;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", referencedColumnName = "user_id")
private List<Location> locations;
}
and then i have
#Repository
public interface UserRepository extends JpaRepository<User, UUID> {
User save(User user);
}
userRepository.save(user) - this method return User with all locations which referenced to this user.
I want to avoid this join query in sql since there could be a lot of referenced locations to the user. This could be cause of OutOfMemory etc. So, i want to get only User without List locations after
userRepository.save(user)
I tried to write query this way:
#Query(nativeQuery = true,
value = "update users " +
"set email = :email, first_name = :firstName, second_name = :secondName " +
"where user_id = :userId " +
"returning user_id, email, first_name, second_name;")
User update(
#Param("userId") #NonNull String userId,
#Param("firstName") #NonNull String firstName,
#Param("secondName") #NonNull String secondName,
#Param("email") #NonNull String email
);
but got the same result with locations in response from DB
This is a hibernate log:
Hibernate:
/* dynamic native SQL query */ update
users
set
email = ?,
first_name = ?,
second_name = ?
where
user_id = ? returning user_id, email, first_name, second_name;
Hibernate:
select
locations0_.user_id as user_id5_0_0_,
locations0_.location_id as location1_0_0_,
locations0_.location_id as location1_0_1_,
locations0_.created_at as created_2_0_1_,
locations0_.latitude as latitude3_0_1_,
locations0_.longitude as longitud4_0_1_,
locations0_.user_id as user_id5_0_1_
from
locations locations0_
where
locations0_.user_id=?

Most probably, you get the locations field loaded due to debugging. Debugging triggers get method and it makes hibernate to load all locations.

Related

How can I map an object to Java Object through SQL native query using Jpa Repository?

I tried several solutions to my SQL query but it seems like I miss something.
I want to get a List<Product> from a nativeQuery.
And I have a relationship between my User entity and Product entity as One to Many.
Here is my both entites -> Product
#Entity
#Data
#Table(name = "product")
#NoArgsConstructor
#AllArgsConstructor
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#CreationTimestamp
#Column(updatable = false)
private Timestamp createdDate;
#UpdateTimestamp
private Timestamp lastModifiedDate;
private String imageURL;
private Long productCode;
#Size(min = 3,max = 100)
private String productName;
#Size(min = 5,max = 100)
private String details;
private BigDecimal price;
private ProductCategory productCategory;
}
User ->
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true,nullable = false)
private String phoneNumber;
#Size(min = 5, max = 25, message = "Username length should be between 5 and 25 characters")
#Column(unique = true, nullable = false)
private String userName;
#CreationTimestamp
#Column(updatable = false)
private Timestamp createdDate;
#UpdateTimestamp
private Timestamp lastModifiedDate;
#Column(unique = true, nullable = false)
#NotNull
private String email;
#Size(min = 5, message = "Minimum password length: 5 characters")
#NotNull
private String password;
#OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
private List<Product> products;
#Transient
#OneToMany(fetch = FetchType.LAZY,mappedBy = "product",cascade = CascadeType.ALL,orphanRemoval = true)
private List<ProductInquiry> productInquiries;
private Role role;
}
Here in this query I need to return all products associated with the given user_id.
#Query(value = "SELECT new egecoskun121.com.crm.model.entity.Product(p.ID,p.CREATED_DATE,p.LAST_MODIFIED_DATE,p.IMAGEURL,p.PRODUCT_CODE,p.PRODUCT_NAME,p.DETAILS,p.PRICE,p.PRODUCT_CATEGORY) FROM PRODUCT AS p WHERE {SELECT PRODUCT_ID FROM USERS_PRODUCTS WHERE USER_ID=:id }",nativeQuery = true)
List<Product> findAllProductsById(#Param("id")Long id);
The problem is that you are using a HQL query but you've set native=true. Setting native=true means that you want to run a SQL query.
This HQL query should work:
#Query("select p from User u join u.products p WHERE u.id = :id")
List<Product> findAllProductsById(#Param("id")Long id);

#ColumnTransformer isn't used in select

[EDIT] Found the solution :
The problem was that the primary key is the field username. It seems that Hibernate doesn't handle this case. So I switched the primary key to an another field to fix it and it works !
I'm trying to use encryption on my database but I have some problems when I want to get the data.
The column transformer isn't used in the select clause but is working in the where clause.
This error appears when the query is a select * of the table like :
public interface UserRepository extends JpaRepository<User, String> {
#Query(value = "select a from User as a where username = :username")
AdminUser findByUsername(#Param("username") String username);
}
(The #Query is useless in this case but it's to have an example)
The generated query by Hibernate looks like :
SELECT user0_.username AS username1_0_, user0_.creation_date AS creation2_0_, user0_.last_update_date AS last3_0_, user0_.enabled AS enabled4_0_, AES_DECRYPT(user0_.email, unhex('myKey'),'myVector') AS email5_0_, user0_.password AS password6_0_, user0_.password_expiration_date AS password7_0_, user0_.profil_id AS profil8_0_
FROM my_user_table user0_
WHERE AES_DECRYPT(user0_.username, unhex('myKey'), 'myVector') = 'userTest';
In case of a query like :
#Query(value = "select a.username from User as a where username = :username")
The select clause is decrypted.
Any ideas why Hibernate doesn't use the columnTransformer in the select ?
Thanks,
#Table(name = "my_user_table")
public class User extends AbstractEntity implements UserDetails {
#Id
#Column(name = "username", nullable = false, unique = true, columnDefinition = "${encryption.column.definition}")
#ColumnTransformer(
forColumn = "username",
read ="${User.username.read}",
write ="${User.username.write}")
private String username;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "email", nullable = false, columnDefinition = "${encryption.column.definition}")
#ColumnTransformer(
read ="${User.email.read}",
write ="${User.email.write}")
private String mailAddress;
#Column(name = "password_expiration_date", nullable = false)
private Date passwordExpirationDate;
#OneToMany(targetEntity = OldPassword.class, fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, orphanRemoval = true)
#JoinColumn(name = "username", nullable = true)
#OrderBy("creationDate desc")
private List<OldPassword> oldPasswords;
/**
* Use this field to enable or disable an account manually
*/
#Column(name = "enabled", columnDefinition = "bit default 0", nullable = false)
private boolean enabled;
#Transient
private Collection<? extends GrantedAuthority> authorities;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "profil_id", referencedColumnName = "id")
private Profil profil;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "user_places", joinColumns = #JoinColumn(name = "username"))
private List<String> userPlaces;
}

Spring create DB repository for SQL table

I have 2 tables in MySql and I have mapped them using hibernate in Spring: users and roles .
I have created one more table: user_roles but I don't know how to map it in hibernate.
You can see the table structure below:
CREATE TABLE users (
username varchar(30) NOT NULL,
email varchar(50) NOT NULL,
password varchar(255) NOT NULL,
first_name varchar(40) NOT NULL,
last_name varchar(40) NOT NULL,
date_of_birth Date,
phone_number varchar(20),
PRIMARY KEY (username)
);
CREATE TABLE roles (
role_id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(255),
PRIMARY KEY (role_id)
);
CREATE TABLE user_roles (
username VARCHAR(30) NOT NULL,
role_id INTEGER NOT NULL,
PRIMARY KEY (username, role_id)
);
Here is the mapping for the roles tables:
#Entity
#Table(name = "roles")
public class Role implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="role_id")
private Integer id;
private String name;
}
Here is the mapping for the users table:
#Entity
#Table(name = "users")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User implements Serializable {
#Id
#NotEmpty
#Size(min = 5, max = 15)
#Column(name = "username")
private String username;
#Email
#NotEmpty
#Column(name = "email")
private String email;
#NotEmpty
#Size(min = 5)
#Column(name = "password")
private String password;
#NotEmpty
#Size(max = 40)
#Column(name = "first_name")
private String firstName;
#NotEmpty
#Size(max = 40)
#Column(name = "last_name")
private String lastName;
...
}
I have created the POJO for the user_role table, but I don't know how to use hibernate on it, I am using #EmbeddedId annotation but it is not working. I don't know how to show the 2 classes above that they are embeddable
#Entity
#Table(name = "user_roles")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UserRole implements Serializable {
public UserRole() { }
public UserRole(User username, Role role_id) {
this.username = username;
this.role_id = role_id;
}
private static final long serialVersionUID = -2947211066236048069L;
#EmbeddedId
private User username;
#EmbeddedId
private Role role_id;
}
How can I map the "UserRole" class to 'user_role' in hibernate? Thank you!
There are two different ways which you can map user_roles table, which I suggest the first one :
1.
#Entity
#Table(name = "users")
public class User
{
#ManyToMany(targetEntity = Role.class)
#JoinTable(name = "users_roles", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
#NotAudited
private Set<Role> roles = new HashSet<>();
}
2.
#Embeddable
public class UserRoleId implements java.io.Serializable
{
#Column(name = "user_id", nullable = false)
private long userId;
#Column(name = "role_id", nullable = false)
private long roleId;
public UserRoleId()
{
}
public UserRoleId(long userId, long roleId)
{
this.userId = userId;
this.roleId = roleId;
}
//hashcode equal
}
Then create the entity.
#Entity
#Table(name = "users_roles")
public class Userroles implements java.io.Serializable
{
#EmbeddedId
private UserRoleId id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
#NotNull
private Users users;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role_id", nullable = false, insertable = false, updatable = false)
#NotNull
private Role role;
}

How to convert a SQL query to Spring JPA query

I have a SQL query like this:
"Select UIProfileID from UserTable where UPPER(UserID) = UPPER('?1')".
I want to convert it to Spring JPA.
I want to write getUIProfileId() and return Integer. But I don't know how to implement. Because User table doesn't have UIProfileId column that it was joined from UIProfileTable table. Please help me solve it.
Currently, I have tables:
User.java
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(name = "UserTable")
public class User {
#Column(name = "UserID", length = 32, nullable = false)
#Id
private String name;
#ManyToOne
#JoinColumn(name = "DomainID", nullable = false)
private Domain domain;
#Column(name = "Password", length = 32, nullable = false)
private String password;
#ManyToOne
#JoinColumn(name = "UIProfileID", nullable = false)
private UIProfile uiProfile;
#Column(name = "ResPerpage", nullable = false)
private Integer resperpage;
#Column(name = "DefaultTab")
private Integer defaulttab;
#ManyToOne
#JoinColumn(name = "AdminProfile")
private AdminProfiles adminProfile;
#Column(name = "LanguageId")
private Integer languageId;
}
UIProfile.java
#Entity
#Getter
#Setter
#Table(name = "UIProfileTable")
public class UIProfile implements Serializable {
#Id
#Column(name = "UIProfileID", length = 11, nullable = false)
private Integer id;
#Column(name = "UIProfileName", length = 32, nullable = false)
private String name;
#OneToMany(mappedBy = "id.uiProfile")
private List<UIProfileTopLevel> topLevels;
}
UserRepository.java
public interface UserRepository extends Repository<User, String> {
Optional<User> findOne(String name);
#Query("Select UIProfileID from User where UPPER(UserID) = UPPER('admin')")
Integer getUIProfileId();
}
You can try this:
#Query("SELECT u.uiProfile.id from User u where UPPER(u.name)=UPPER('admin')")
Integer getUIProfileId();
Here User is the domain class name and u is the reference of User. with u we will access User's field NOT the column name which are specified with #Column or #JoinColumn Ex : #JoinColumn(name = "UIProfileID", nullable = false).

How to fetch data from multiple table query by hibernate?

I have a user management application that allocate each user a team and one or many access to different application. Now for the reporting page I am trying to fetch data from two table (UserInfo & UserAppAccess) by Hibernate but I can't.
Here are the tables :
Table 1 (UserInfo):
#Entity
#Table(name = "user_info", uniqueConstraints = { #UniqueConstraint(columnNames = "username"), #UniqueConstraint(columnNames = "email") })
public class UserInfo implements java.io.Serializable {
public enum UserStatus {
active, inactive
}
public enum UserType {
user, creator
}
private static final long serialVersionUID = 2650114334774359089L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 100)
private String id;
#Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
#Column(name = "password", nullable = false, length = 80)
private String password;
#Column(name = "status", nullable = false, length = 10)
#Enumerated(EnumType.STRING)
private UserStatus status;
#Column(name = "type", nullable = false, length = 10)
#Enumerated(EnumType.STRING)
private UserType type;
#Column(name = "phone", nullable = true, length = 30)
private String phone;
#Column(name = "email", nullable = true, length = 50)
private String email;
#Column(name = "first_name", nullable = true, length = 50)
private String firstName;
#Column(name = "last_name", nullable = true, length = 50)
private String lastName;
#Column(name = "login", nullable = true, length = 100)
private long login;
#Column(name = "alert", nullable= true, length=500)
private String alert;
#OneToOne
#JoinColumn(name = "team_id")
private Team team;
}
Table 2 (Team):
#Entity
#Table(name = "team", uniqueConstraints = { #UniqueConstraint(columnNames = "team_name"), #UniqueConstraint(columnNames = "team_code") })
public class Team implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#Column(name = "team_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "team_code", unique = true, nullable = false, length = 10)
private String code;
}
Table 3 (Access):
#Entity
#Table(name = "access_def")
public class Access implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#Column(name = "access_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "access_code", unique = true, nullable = false, length = 10)
private String code;
}
Table 4 (Application):
#Entity
#Table(name = "application", uniqueConstraints = { #UniqueConstraint(columnNames = "name") })
public class Application implements java.io.Serializable {
private static final long serialVersionUID = 5803631085624275364L;
#Id
#Column(name = "name", nullable = false, length = 100)
private String name;
}
Table 5 (UserAppAccess):
#Entity
#Table(name = "user_app_access")
#Embeddable
public class UserAppAccess implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#OneToOne
#JoinColumn(name = "user_id")
private UserInfo userInfo;
#Column(name = "app_name", nullable = false, length = 100)
private String appName;
#OneToOne
#JoinColumn(name = "access_id")
private Access access;
}
I have a report page that allow Admin to select multiple options (for example: list all active users in Team test and application APP1).
here is my code to fetch the data but it is not working :
public List<?> getReport(String teamId,String appName,UserStatus active,UserStatus inactive) {
Session session = sessionFactory.getCurrentSession();
String hql = "SELECT u.firstName,u.username,u.status,u.lastName,u.phone,u.team From UserInfo u,AppAccess a WHERE u.status =? OR u.status =? AND u.team.id = ? AND a.appName = :appName ";
Query query = session.createQuery(hql);
query.setParameter(0, active);
query.setParameter(1, inactive);
query.setParameter(2, teamId);
query.setParameter("appName", appName);
System.out.println(query.list());
return query.list();
}
For instance when I pass
Active Users: Active
inactive User:null
team:test
application :app1
teamId :28f66133-26c3-442b-a071-4d19d64ec0aeappName :app1active :activeinactive:null
I am getting this back from my return query.list();
[[Ljava.lang.Object;#2961116f, [Ljava.lang.Object;#23bfa3a2, [Ljava.lang.Object;#7a8ff303, [Ljava.lang.Object;#9b88d2, [Ljava.lang.Object;#6333934d, [Ljava.lang.Object;#4f0bd71c, [Ljava.lang.Object;#125797cf, [Ljava.lang.Object;#34afa071, [Ljava.lang.Object;#764e75bc, [Ljava.lang.Object;#1913c652, [Ljava.lang.Object;#61413e5a, [Ljava.lang.Object;#264b898, [Ljava.lang.Object;#22930462, [Ljava.lang.Object;#6204cfa9, [Ljava.lang.Object;#29dd9285, [Ljava.lang.Object;#11be6f3c, [Ljava.lang.Object;#6d78d53d, [Ljava.lang.Object;#17f7cff1, [Ljava.lang.Object;#e74e382, [Ljava.lang.Object;#1c047338, [Ljava.lang.Object;#68286fe6, [Ljava.lang.Object;#36ca9a76, [Ljava.lang.Object;#2f62d514, [Ljava.lang.Object;#1932c5a, [Ljava.lang.Object;#6544c984, [Ljava.lang.Object;#70a2d0d, [Ljava.lang.Object;#2d13b417, [Ljava.lang.Object;#6894691f, [Ljava.lang.Object;#6781a7dc, [Ljava.lang.Object;#7133919a]
I'd suggest using native SQL and JDBC for reporting (see
How should I use Hibernate Mapping while dealing with huge data table)
For performance reasons it is desirable to create view model objects from result set right in the DAO. It may looks like mixing levels of abstraction (view layer with persistence layer), but it's ok when you need to fetch a big amounts of data and don't want to make unnecessary object transformations from persistence models to view models.
If you're want to stuck with hibernate, you may define a syntetic entity and map if on a view, containing only necessary columns from multiple:
#Entity
#Table("V_USER_REPORT")
public class UserAppData {
// columns from table "user"
#Id
#Column(name = "id", unique = true, nullable = false, length = 100)
private String id;
#Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
// columns from table "user"
#Column(name = "app_name", nullable = false, length = 100)
private String appName;
// columns from table "team"
#Column(name = "team_id", unique = true, nullable = false, length = 80)
private String team_id;
#Column(name = "team_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "team_code", unique = true, nullable = false, length = 10)
private String code;
// and so on...
}
Then you fetch such entity by parameters as you do it with normal entity.
By adding LEFT JOIN FETCH or FETCH ALL PROPERTIES. This will force JOINS insteed of lazy initialization
String hql = "SELECT u.firstName,u.username,u.status,u.lastName,u.phone,u.team From UserInfo u,AppAccess a FETCH ALL PROPERTIES WHERE u.status =? OR u.status =? AND u.team.id = ? AND a.appName = :appName ";
More information can be found in HQL Documentation
Firstly: I hope each of your entity classes have a toString() method (can be auto-generated with eclipse) so you can print them. Printing object reference isn't enough to infer whether/not you're getting what you want.
Secondly, the syntax of HQL joins is normally like this:
String queryString = "select distinct f from Foo f inner join foo.bars as b" +
" where f.creationDate >= ? and f.creationDate < ? and b.bar = ?";
(taken from How do you create a Distinct query in HQL)

Categories

Resources