HashSet in mapRow for JDBC template - spring-boot

I would like to make a select from one of my user table. In this table there is a field, which is a HashSet. This Set has "role" objects. I need a unique mapRow method for this operation, but I don't know how to to do it with the Set.
You can see my code below:
#Entity
#Getter
#Setter
#ToString
public class User {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
#Column( nullable=false )
private String password;
#Column( unique=true, nullable=false )
private String email;
private Boolean active;
#ManyToMany()
#JoinTable(name="role_user")
private Set<Role> roles;
}
public class UserMapper implements RowMapper<User> {
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("email"));
user.setActive(rs.getBoolean("active"));
//user.setRoles();
return user;
}
}

Related

Spring, JPA: How to query for Entities under another Entity with a many-to-many relationship bridge table setup

I'm fairly new to Spring. I'm trying to query all the donations under one donor with this ERD:
Donor |----* Agreement *----| Donations (A many-to-many relationship that uses a bridge table)
Here's my code:
Donor.java
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Donor extends Auditable implements Comparable<Donor>{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Cannot have an empty account number field.")
private String accountNumber;
private String accountName;
private String salutation;
private String donorName;
private String cellphoneNumber;
private String emailAddress;
private String companyTIN;
private String phone1;
private String phone2;
private String faxNumber;
private String address1;
private String address2;
private String address3;
private String address4;
private String address5;
private String companyAddress;
private LocalDate birthDate;
private String notes;
#OneToMany(mappedBy = "donor")
List<MOA> moaList = new ArrayList<>();
...
}
Donation.java
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Donation extends Auditable implements Comparable<Donation> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Cannot have an empty account number field.")
private String accountNumber;
private String accountName;
private String orNumber;
private String date;
private Double amount;
private String notes;
private String needCertificate;
private String purposeOfDonation;
#OneToMany(mappedBy = "donation")
List<MOA> moaList = new ArrayList<>();
...
}
MOA.java (Agreement)
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class MOA extends Auditable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "donor_id")
#JsonIgnoreProperties("moaList")
private Donor donor;
#ManyToOne
#JoinColumn(name = "donation_id")
#JsonIgnoreProperties("moaList")
private Donation donation;
private String name;
private String donorAccountNumber;
private Long foreignDonationId;
private LocalDate dateSigned;
}
In my DonorRepository I'm trying to make this query which I expected would give me what I want:
public interface DonorRepository extends JpaRepository<Donor, Long> {
...
#Query(value = "SELECT * FROM donor WHERE account_number = ?1", nativeQuery = true)
List<Donation> findDonorsDonations(String accountNumber);
...
This gives me an error
Failed to convert from type [java.lang.Object[]] to type [com.package.server.domain.Donation] for value '{1, admin, 2021-04-01 10:29:53.0, admin, 2021-04-01 10:29:53.0, School, 123456, null, null, null, null, null, null, null, null, null, John Doe, null, null, null, null, null, Mr.}'; nested exception is org
You can use specification api and SpecificationExecutor.
You have to Join Donation with MAO(MAO with Donor) then query for donations of a particular Donor.

Hibernate JPA loop

I created an entity class :
#Entity
#Table(name="users")
#Getter #Setter
public class UserModel implements Serializable {
#Setter(AccessLevel.NONE)
#Getter(AccessLevel.NONE)
private static final long serialVersionUID = -5608230793232883579L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false, unique = true)
private String userId;
#Column(nullable = false, length = 50)
private String firstName;
#Column(nullable = false, length = 50)
private String lastName;
#Email
#Column(nullable = false, length = 120, unique = true)
private String email;
#Column(nullable = false)
private String encryptedPassword;
private Boolean emailVerificationStatus = false;
private String emailVerificationToken;
#ManyToMany(cascade= { CascadeType.PERSIST }, fetch = FetchType.EAGER )
#JoinTable(
name = "user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns=#JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<RoleModel> roles;
#JsonManagedReference
#OneToMany(mappedBy = "user")
private List<ProjectModel> projects;
}
For the list of projects, I also have an entity class:
#Entity
#Table(name= "projects")
#Getter #Setter
public class ProjectModel implements Serializable {
#Setter(AccessLevel.NONE)
#Getter(AccessLevel.NONE)
public static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false, unique = true)
private String projectId;
// ...
#Column
#JsonManagedReference
#OneToMany(mappedBy = "project")
private List<ObjectiveModel> objectives;
// ...
#JsonBackReference
#ManyToOne(
cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH },
fetch = FetchType.LAZY
)
private UserModel user;
}
I also use a DTO layer to communicate with database:
#Getter #Setter
public class UserDto implements Serializable {
#Setter(AccessLevel.NONE)
#Getter(AccessLevel.NONE)
private static final long serialVersionUID = -5352357837541477260L;
// contains more information than models used for rest
private long id;
private String userId;
private String firstName;
private String lastName;
private String email;
private String password;
private String encryptedPassword;
private String emailVerificationToken;
private Boolean emailVerificationStatus = false;
private List<String> roles;
private List<ProjectDto> projects;
}
Each entity has its own Dto equivalent. I can create a user. My issue is trying to log in. My userServiceImpl implements Spring Security UserService. Here is my implementation :
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserModel userModel = userRepository.findByEmail(email);
if(userModel == null)
throw new UsernameNotFoundException("User with email " + email + " not found");
return new UserPrincipalManager(userModel);
}
My UserPrincipalManager :
public class UserPrincipalManager implements UserDetails {
private static final long serialVersionUID = 7464059818443209139L;
private UserModel userModel;
private ProjectModel projectModel;
#Getter #Setter
private String userId;
#Autowired
public UserPrincipalManager(UserModel userModel) {
this.userModel = userModel;
this.userId = userModel.getUserId();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new HashSet<>();
Collection<AuthorityModel> authorityModelEntities = new HashSet<>();
// get user roles
Collection<RoleModel> roleModels = userModel.getRoles();
if (roleModels == null) {
return authorities; // null
}
// get user roles
roleModels.forEach((role) ->{
authorities.add(new SimpleGrantedAuthority(role.getName()));
authorityModelEntities.addAll(role.getAuthorities());
});
// get user authorities
authorityModelEntities.forEach(authorityModel -> {
authorities.add(new SimpleGrantedAuthority(authorityModel.getName()));
});
return authorities;
}
#Override
public String getPassword() {
return this.userModel.getEncryptedPassword();
}
#Override
public String getUsername() {
return this.userModel.getEmail();
}
// we do not store this information in DB
#Override
public boolean isAccountNonExpired() {
return true;
}
// we do not store this information in DB (yet)
#Override
public boolean isAccountNonLocked() {
return true;
}
// we do not store this information in DB (yet)
#Override
public boolean isCredentialsNonExpired() {
return true;
}
// isEnabled depending if account is activated => email verification status value
#Override
public boolean isEnabled() {
return this.userModel.getEmailVerificationStatus();
}
}
While trying to log in a User sql request is looping.
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106)
In the end the application crashes and returns a 403 error.
2020-10-05 12:07:22.215 DEBUG 4564 --- [nio-8080-exec-8] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
The login fonction works if user do not have project associated.
I don't know anything about model mapper, but I would like to provide you an alternative solution because I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(UserModel.class)
public interface UserDto extends Serializable {
#IdMapping
Long getId();
String getUserId();
String getFirstName();
String getLastName();
String getEmail();
String getPassword();
String getEncryptedPassword();
String getEmailVerificationToken();
Boolean getEmailVerificationStatus();
Set<String> getRoles();
Set<ProjectDto> getProjects();
#EntityView(ProjectModel.class)
interface ProjectDto {
#IdMapping
Long getId();
String getProjectId();
// Other mappings...
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
The big bonus here, it will only fetch the columns that are actually needed and it validates the DTO model against your JPA model during boot time, so there are no more runtime surprises!

Why I can't delete data in cascade way?

The problem is when I want to delete user I'm getting error in Spring Boot like that:
java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (32506632_pam.badge, CONSTRAINT FK4aamfo6o0h5ejqjn40fv40jdw FOREIGN KEY (user_id) REFERENCES user (id))
I'm guessing that I need to delete data in cascade way. So I've placed CascadeType.REMOVE value to #OneToOne annotation like that, but it doesn't work:
badge entity
#Entity
#Data
#Table(name = "badge")
#AllArgsConstructor
#NoArgsConstructor
public class Badge {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonManagedReference
#ManyToMany(mappedBy = "badges", fetch = FetchType.LAZY)
private List<Reader> readers;
#OneToOne(cascade = CascadeType.REMOVE, orphanRemoval=true)
#JoinColumn(name = "user_id")
private User user;
private String number;
#Lob
#Basic(fetch = FetchType.LAZY)
private byte[] photo;
}
user entity
#Entity
#Data
#Table(name = "user")
#AllArgsConstructor
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String lastname;
private String pesel;
private String email;
private String telephone;
private Integer age;
private String gender;
}
reader entity
#Entity
#Data
#Table(name = "reader")
#AllArgsConstructor
#NoArgsConstructor
public class Reader {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonBackReference
#ManyToMany(fetch = FetchType.LAZY)
private List<Badge> badges;
private String department;
private String room;
private Boolean status;
}
Class which loads initial data
#Component
public class DataLoader implements ApplicationRunner {
#Autowired
private UserService userService;
#Autowired
private BadgeService badgeService;
#Autowired
private ReaderService readerService;
#Override
public void run(ApplicationArguments args) throws Exception {
User user1 = new User(null, "Jan", "Kowal", "11111111111", "jan#kowal.pl", "+48111111111", new Integer(23), "male");
userService.saveUser(user1);
Reader reader1 = new Reader(null, null, "Warehouse", "207A", new Boolean("true"));
Badge badge1 = new Badge(null, Arrays.asList(reader1), user1, "738604289120", null);
badgeService.saveBadge(badge1);
reader1.setBadges(Arrays.asList(badge1));
readerService.saveReader(reader1);
}
}
Endpoint for deleting user - it uses repository which extends CrudRepository and uses default delete behavior.
#DeleteMapping("/deleteUserById/{id}")
private void deleteUserById(#PathVariable Long id) {
userService.deleteUserById(id);
}
Database structure in phpmyadmin
My goal is to delete user and associated badge with him, then to delete row in reader_badges table.

How get User to email?

I need get user to email with jpa
i have this code:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(#Param("email") String email);
User findById(#Param("id") Long id);
}
When i tried this :
User owner = userRepository.findById(user.getId()); //yes
User user=userRepository.findByEmail(email);//no
My class User is:
#Entity
#Table(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = -3009157732242241606L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "name")
private String name;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
public User() {
}
//getters and setters
}
I want to obtain a user through the user's email, if I can obtain it through his id but I also need to obtain it through email
In you Model class "email" is not an unique so that it will return multiple rows to avoid that add
#Entity
#Table(name = "User")
public class User implements Serializable {
.....
#Column(unique=true,...)// .. add your extra
private String email;
.....
}
If you want top 1 result to be returned you could change the method
User findByEmail(#Param("email") String email);
with
User findOneByEmail(String email);

Retrieve an object which has a POJO as a primary key with Spring JPA

I have the following classes: DepartmentMember and Account, mapped by a OneToOne relationship.
This is the DepartmentMember class:
#Entity(name="departmentmember")
#Table(name="departmentmember")
#Embeddable
public class DepartmentMember {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="name", nullable=false)
private String nume;
#Column(name="lastName", nullable=false)
private String prenume;
#OneToOne(mappedBy="departmentMember",cascade=CascadeType.ALL,fetch=FetchType.LAZY, optional=false)
#JsonIgnore
private Account account;
public DepartmentMember() {}
public DepartmentMember(String nume, String prenume, String cNP, String email) {
super();
this.nume = nume;
this.prenume = prenume;
}
//getters and setters
}
And this is the Account class :
#Entity(name="users")
#Table(name="users")
public class Account {
#Id
private int id;
#Column(name="username", unique=true, nullable=false)
private String username;
#Column(name="password", nullable = false)
private String password;
#Column(name="authorities", nullable=false)
private String authorities;
#OneToOne(fetch=FetchType.EAGER)
#MapsId
#Embedded
private DepartmentMember departmentMember;
public Account() {}
public Account(String username, String password, String authorities) {
super();
this.username = username;
this.password = password;
this.authorities = authorities;
}
//getters and setters
}
I have defined an interface AccountRepository which extends the CrudRepository interface provided by Spring JPA.
What I want to do is define a query, which takes as a parameter a DepartmentMember id and retrieves the associated account for that member. Now this is how an Account object looks like:
{
"username": "Maria_Popescu",
"password": "4ec38c6e-2463-4562-99ba-9f6c2b4528c4",
"authorities": "ROLE_USER",
"departamentMember": {
"id": 2,
"nume": "Popescu",
"prenume": "Maria",
}
I tried using the findOne(int id) method, but it didn't work, so which is the correct approach to solve this?
Edit:
In the AccountRepository I have defined the following method :
Account findByDepartmentMemberId(int id) and I still get a not found error.
There was actually another problem in my controller. I managed to get it working by adding
Account findByDepartmentMemberId(#Param("id")int id);
in the AccountRepository

Resources