How can I fix a `LazyInitializationException`? - spring

My User & Transfer entities:
#Entity
#Table(name = "user")
public class User {
// [...]
#OneToMany(mappedBy = "user")
private Collection<Transfer> transfers;
}
#Entity
#Table(name = "transfer")
public class Transfer {
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name="user_id", updatable=false)
private User user;
}
Where I get the LazyInitializationException:
public void transferToFriend(Requirement requirement) {
Optional<User> optionalUser = userRepository.findById(requirement.getUserId());
if (optionalUser.isPresent() && !Objects.isNull(recipient)) { // LazyInitializationException
// [...] }
}
Console LazyInitializationException:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.sample.model.User.transfers, could not initialize proxy - no Session
What I've tried:
fetch="EAGER"
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=false
EDIT
I discovered that two #Override methods caused the issue in User:
#Override
public String toString() {
return "User{" +
"id=" + id +
", email='" + email + '\'' +
", password='" + password + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", ibanCode=" + ibanCode +
", bicCode=" + bicCode +
", balance=" + balance +
", friendsList='" + friendsList + '\'' +
", transfers=" + transfers +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return ibanCode == user.ibanCode && bicCode == user.bicCode && Double.compare(user.balance, balance) == 0 && Objects.equals(id, user.id) && Objects.equals(email, user.email) && Objects.equals(password, user.password) && Objects.equals(firstName, user.firstName) && Objects.equals(lastName, user.lastName) && Objects.equals(friendsList, user.friendsList) && Objects.equals(transfers, user.transfers);
}
Those are needed in my Spring Boot unit tests. How should I fix it?

The default for the #OneToMany annotation is FetchType.LAZY so your collections are loaded lazily.
In order to be able to access the collection after you've retrieved the object you need to be in a transactional context (you need an open session)
When you call:
Optional<User> optionalUser = userRepository.findById(requirement.getUserId());
internally a new session is created, the object is retrieved and as soon as the findById method returns the session is closed.
What you should do is make sure you have an open session whenever you access the lazy collection in your User object (which both the equals method and toString do).
The simplest way is to annotate the transferToFriend method with #Transactional and make sure you call the method from outside your service otherwise (because of the way AOP proxies are handled) no transaction will be created (hence no session).

Related

I can't get an entity ID in spring boot

I am learning Spring-Boot and I'm doing a little project to practice, but I have a problem.
This is a simple authentication app, you can register and log in. My aim is: If you log in your username should be appeared, and for further functions I need the ID as well.
So I have this code:
#PostMapping("/main")
public String login(#ModelAttribute Users user, Model model) {
time = sdf.format(new Date());
Users correctUser = serv.selectUser(user.getName(), user.getPassword());
if (correctUser != null) {
//Users data
login_name = user.getName();
actual_user_id = user.getId();
model.addAttribute("given_name", login_name);
System.out.println("DEBUG: " + user);
System.out.println(time + " Successful");
return "main_screen";
} else {
System.out.println(time + " Log in failed");
return "error_page";
}
}
I can get and storage the name well in login_name, but with the ID I have some problems. As you can see I use user.getId() same as with name, but either way I get null and can't storage the ID in my actual_user_id variable.
Here is my repository:
#Repository
public interface UserRepository extends JpaRepository<Users, Integer> {
Optional<Users> findFirstByName(String name);
Optional<Users> findUserByNameAndPassword(String name, String password);
}
And my service method:
public Users authentication(String name, String password) {
return repo.findUserByNameAndPassword(name, password).orElse(null);
}
EDIT: And this is my Users class
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String password;
private String email;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", passowrd='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
I think it should work, but I can't find the problem.
Can anyone help me?
As I can see, I get the name and the password with the findUserByNameAndPassword() and nothing else, however I should I suppose.
You look to be trying to get your id from the user passed to you in the post request:
actual_user_id = user.getId();
Try getting your information from the user you retrieved from the database:
actual_user_id = correctUser.getId();

Not persisting entity with thymeleaf, spting data and r2dbc

Im trying to create Skill entity through Thymeleaf form and post it:
controller's endpoint
#PostMapping("/add")
public String addSkill(#ModelAttribute Skill skill){
log.info( " orly? "+ (skill== null));
log.info(skill.getName() + " name was set");
log.info(skill.getId() + " i<--d");
log.info(skill.getLevel() + " level was set");
log.info(skill.getPriority() + " prior was set");
log.info(skill.getSkillGroupName() + " group id was set");
service.add(skill);
return TEMPLATE;
}
service's method
#Override
public Mono<Skill> add(Skill skill) {
log.debug("SKILL IS ______ "+ skill.getName() + " ____WAS SAVED");
return repository.save(skill);
}
repo
#Repository
public interface SkillRepository extends ReactiveCrudRepository<Skill, UUID> {
Mono<UUID> removeById(UUID id);
}
entity implements Persistable
#Data
#Table
#NoArgsConstructor
#AllArgsConstructor
public class Skill implements Persistable<UUID> {
#Id
private UUID id;
#Column("skill_name")
private String name;
private Level level;
private Priority priority;
#Column("skill_group_name")
private String skillGroupName;
#Override
public boolean isNew() {
boolean result = Objects.isNull(id);
this.id = result ? UUID.randomUUID() : this.id;
return result;
}
}
Main class is annotated with #EnableR2dbcRepositories.
When I submit form, I get the log, confirming that entity is not null, all fields but id aren't nulls. And that's all, service's method add(Skill skill) never produce logs, neither postgres shows tuple. Any ideas?

Spring data projection for native query not mapped to interface

This my repostoriy for retrieve items
#Query(value = "SELECT DISTINCT M.ID as \"id\", "
+ " M.NAME_PRIMARY_LANG as \"name\" "
+ " FROM ECOMMERCE_CORE.MERCHANT_ITEMS M , "
+ " ECOMMERCE_CORE.PRODUCT_UNIT_OF_MEASURE P , "
+ " ECOMMERCE_CORE.LOOKUP_TYPES_STATUS S , "
+ " ECOMMERCE_CORE.ITEM_TYPES T , "
+ " ECOMMERCE_CORE.ITEM_PRICE I,"
+ " ECOMMERCE_CORE.MERCHANT_ITEM_BRAND B, "
+ " ECOMMERCE_CORE.MERCHANT_ITEM_CATEGORY C "
+ " WHERE M.ID = P.PRODUCT_ID AND M.ID=I.PRODUCT_ID AND M.ID = B.MERCHANT_ITEM_ID AND S.ID=M.STATUS_ID AND M.TYPE = T.ID AND M.MERCHANT_ID =?1 AND M.STATUS_ID =?2 "
+ " AND P.BRANCH_ID = ?3 AND I.CHANNEL_ID = ?4 ",
nativeQuery = true
)
List<ItemModelProjection> findBySupplierIdAndStatusCode(long id, long status, long branchId, long channelId, Pageable pageable);
and this my interface which i need to map the result to it
#Getter
#EqualsAndHashCode(of = {"id"})
public class ItemModelProjection {
private String id;
private String name;
public ItemModelProjection(final String id, final String name) {
this.id = id;
this.name = name;
}}
and the result of this query not mapped to the interface , what is the problem for it ?
You can solve this issue and achieve the result by using projections by making your DTO an interface with getters for columns returned by the query.
All you need to do is to have interface and contain query domains starting with get.
public interface ItemModelProjection {
Long getId();
String getName();
}
You need an interface if you want to retrieve those values. And be careful with the naming of the methods. If you have like in your case AS name then call the method getName(). But if you don't have AS specified and you are returning a value for example like PRODUCT_UNIT_OF_MEASURE then use the following method name: getProduct_Unit_Of_Measure().
For getting those two values use the following interface:
public interface ItemModelProjection {
String getId();
String getName();
}

Able to persist data using entitymanager.persist() but unable to get using entitymanager.find() in spring hibernate application

This is my customer entity
#Entity
public class Customer
{
#Id
private String account_no;
private String customer_name,father_name,gender, phone, email, aadhaar; // phone no is varchar because I'll do validation in angular.
private double salary;
// one customer can have one loan. Relation is unidirectional
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="account_no")
private Loan loan;
public Customer() {}
#Override
public String toString() {
return "CustomerInfo [account_no=" + account_no + ", customer_name=" + customer_name + ", phone=" + phone
+ ", email=" + email + ", aadhaar=" + aadhaar + ", salary="
+ salary + ", loan=" + loan + "]";
}
}
Now this is my Loan entity
#Entity
public class Loan
{
#Id
#Column(name="account_no")
private String account_no; // making this as foreign key will not allow any holder to take loan again.
private String type;
private String issue_date;
private String last_payment;
private double loan_amount;
private double emi_amount;
private int emi_balance;
public Loan() {
}
#Override
public String toString() {
return "LoanInfo [account_no=" + account_no + ", type=" + type + ", loan_amount=" + loan_amount
+ ", emi_amount=" + emi_amount + ", emi_balance=" + emi_balance + ", issue_date=" + issue_date + "]";
}
}
Now the code of Dao layer
The findCustomer() function is returning null.
In dao findLoan() and addCustomer() is working fine.Even I've checked database , it is persisting data to database.
Database which I'm using is mysql and hibernate is used to implement the application. Spring mvc is used .
#Repository("bankDao")
#Transactional
public class BankDao {
#PersistenceContext
EntityManager em;
// It add customer details and loan details like emi while sanctioning any loan.
public void addCustomer(Customer customer) throws RevokeLoanException
{
try {
em.persist(customer);
}
catch(Exception e) {
throw new RevokeLoanException();
}
}
// for customer details
public Customer findCustomer(String accNo) throws LoanNotFoundException{
System.out.println(em.find(Customer.class, accNo));
Customer customer=em.find(Customer.class, accNo);
if(customer==null)
throw new LoanNotFoundException();
return customer;
}
// useful while paying emi
public Loan findLoan(String acc_no) throws LoanNotFoundException{
// TODO Auto-generated method stub
Loan loan=em.find(Loan.class, acc_no);
if(loan==null)
throw new LoanNotFoundException();
return loan;
}
Problem arising in controller . Controller is not working in the specific way for id=2020-04-30T23:22:00.210
Api call = http://localhost:8082/LoanBackEnd/loan/loanStatus/2020-04-30T23:22:00.210
#GetMapping("/loanStatus/{id}")
public ResponseEntity<Map<String, String>> loanStatus(#PathVariable String id) throws LoanNotFoundException{
System.out.println(id+" which got");
Map<String,String> response=bankService.loanStatus(id);
return new ResponseEntity<Map<String,String>>(response,HttpStatus.OK);
}
That print line is returning me 2020-04-30T23:22:00 in place of 2020-04-30T23:22:00.210

Spring #PreAuthorize passes null to Service [duplicate]

This question already has an answer here:
Spring - SpEL evaluates entity argument as null reference in #PreAuthorize("hasPermission")
(1 answer)
Closed 5 years ago.
I have an issue with #PreAuthorize and a sevice that checks if the athenticated user may access the searched item.
The one service callDistributionRequest that gets the item is working fine - #PreAuthorize recieves and passes the right distId. The other one updateDistributionRequestExportFileName gets also the right distId and passes it to the distributionRequestService. On the method userBelongsToRecipientOfTheDistributionRequest distId comes as a null
The Spring RestController with the two web services
#RestController
#RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class DistributionRequestRESTController {
#Autowired
private #Getter #Setter DistributionRequestService distributionRequestService;
private final Logger log = LoggerFactory.getLogger(this.getClass());
private String logResponse = " - response: ";
#Autowired
public DistributionRequestRESTController(DistributionRequestService distributionRequestService) {
this.distributionRequestService = distributionRequestService;
}
#RequestMapping(value = Consts.URLDISTRIBUTIONREQUEST + Consts.URLDISTREQID)
public DistributionRequest callDistributionRequest(#PathVariable long distId) {
String loginfo = "get distribution with id: " + distId;
//log.info(loginfo);
DistributionRequest found = distributionRequestService.findOne(distId);
log.info(loginfo + logResponse + JSONParser.toJsonString(found));
return found;
}
#RequestMapping(method = RequestMethod.POST, value = Consts.URLDISTRIBUTIONREQUEST + Consts.URLDISTREQID + Consts.URLUPDATE + Consts.URLFILENAME)
public DistributionRequest updateDistributionRequestExportFileName(
#PathVariable long distId,
#RequestBody String fileName,
#AuthenticationPrincipal UserDetails user) {
String loginfo = user.getUsername() + " try to update filename with : " + fileName;
//log.info(loginfo);
DistributionRequest updated =
distributionRequestService.updateExportFilename(distId, fileName);
log.info(loginfo + logResponse + JSONParser.toJsonString(updated));
return updated;
}
}
The Service interface:
public interface DistributionRequestService {
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest findOne(Long distId);
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest updateExportFilename(Long distId, String filename);
}
And the class that checks if the user may access the searched item
#Service(value = "distributionRequestOwnerService")
public class DistributionRequestOwnerServiceImpl implements DistributionRequestOwnerService {
#Autowired
private AccountService accountService;
#Autowired
private DistributionRequestsRepository distributionRequestsRepository;
#Override
public boolean userBelongsToRecipientOfTheDistributionRequest(Long distId) {
return userBelongsToRecipientOfTheDistributionRequest(distId, null);
}
#Override
public boolean userBelongsToRecipientOfTheDistributionRequest(Long distributionRequestId, String username) {
DistributionRequest distributionRequest = distributionRequestsRepository.findOne(distributionRequestId);
ServiceAccount currentUser;
if (username == null)
currentUser = accountService.getCurrentUser();
else
currentUser = accountService.findByUsername(username);
if (distributionRequest != null
&& distributionRequest.getRecipientId() == currentUser.getRecipientId())
return true;
throw new AercacheWSException(Consts.EXCEPTIONMISSINGELEMENTORPERMITION);
}
}
Any ideas?
thanks in advance
Found the solution duplicate to
as #teppic pointed parameter in interfaces should be annotated.
public interface DistributionRequestService {
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest findOne(#Param("distId") Long distId);
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest updateExportFilename(#Param("distId") Long distId, String filename);
}

Resources