How to throw exceptions on search methods in spring data jpa - spring

I am using spring-data-jpa repositories for database operations. I want to throw exceptions if object doesn't exists in database for all methods in my repositories. For example Consider the following method in OrderRepository
findByCustomerAndPayment(Customer customer, Payment payment);
I want to query all orders based on customerId and paymentId. Both the objects are neccessry in the above query. But spring-data-rest returns null if I gave cutomerId doesn't exists in database. I expect spring-data-rest to throw exception if object doesn't exists in database.
How to achieve this?

You just need orElseThrow
orderRepository.findByCustomerAndPayment(customer, payment).orElseThrow(() -> new ResourceNotFoundException("customer", "id", customer.getId()));

If you're using Java 8, you can use Optional<Order> as the return type of the repository method. If the repository method returns an empty Optional calling get on it will throw a NoSuchElementException. Otherwise there is no support for throwing exceptions by repository methods if there are no results.
try {
Optional<Order> result = repository.findByCustomerAndPayment(customer,payment);
Order order = result.get();
} catch(NoSuchElementException e) {
// do something with the exception
}

You can make custom repository implementation like below:
public interface OrderRepositoryCustom {
Order findByCustomerAndPaymentRequired(Customer customer, Payment payment);
}
public class OrderRepositoryImpl implements OrderRepositoryCustom {
#Autowired
OrderRepository orderRepository;
#Override
public Order findByCustomerAndPaymentRequired(Customer customer, Payment payment) {
Order o = orderRepository.findByCustomerAndPayment(customer, payment);
if(o == null) {
throw new IncorrectResultSizeDataAccessException(1);
}
return o;
}
}
Your OrderRepository interface should extend customized:
public interface OrderRepository extends CrudRepository<Order, Long>, OrderRepositoryCustom {
Order findByCustomerAndPayment(Customer customer, Payment payment);
}
Edited
As IncorrectResultSizeDataAccessException is RuntimeException, then no need to throws declaration - i fixed that.

Use Optional together with orElseThrow.
Order findByCustomerAndPayment(Customer customer, Payment payment);
default Order findByCustomerAndPaymentOrThrow(Customer customer, Payment payment) {
return Optional.ofNullable(findByCustomerAndPayment(customer, payment)).orElseThrow();
};

Related

Get or Create in spring boot

Using spring boot if how I can implement this logic (Get or Create)
Post request I will pass the student details if the student email exist return simple message "email already exist" and if not create new student record
I am using JpaRepository for my DB connection and I do have
Student class has the student structure data setter getter ...etc.
StudentController has registerNewStudent calls studentService.addNewStudent
StudentService has the addNewStudent method
addNewStudent should include some logic to check if the student email exist and if not it should create
What do you think ?
You can create a findByEmail method using JpaRepository. This method means select * from student where email="passedValue". If student with this email exits this method will return that student otherwise it will return null.
public interface StudentRepository extends JpaRepository<Student,String> {
Student findByEmail(String email); }
And then use this method:
public void saveStudent(Student student) {
Student savedStudent = studentRepository.findByEmail(student.getEmail());
if (savedStudent != null) {
System.out.println("email already exist");
} else {
studentRepository.save(student);
}
}
Hope this helps.

I am getting internal server error dont konw reason for that

my code.....this was controller
controller
#Getmapping("/Transferabalance/{id}")
public List<Transaction> transfer(#PathVariable("Accbalance") long Accbalance) throws ResourceNotFoundException
{
List<Transaction> balance=Transservice.findByAccbalance(Accbalance);
if(balance==null)
throw new ResourceNotFoundException("NO BALANCE");
else
return balance;
}
This was service service
public List<Transaction> findByAccbalance(long Accbalance)
{
// TODO Auto-generated method stub
return transrepo.findByAccbalance(Accbalance);
}
transaction repository i used an query to retrive the elemnt from data base
public interface TransactionRepository extends CrudRepository<Transaction,Long>
{
#Query(value="select ACCBALANCE from TRANSACTION",nativeQuery = true)
List<Transaction> findByAccbalance(long Accbalance);
}
1st of all, CrudRepository itself provides you lots of methods to perform operation then why use JPQL though your query is simple.
you can simply use findByAccbalance(long Accbalance) that will return you expected result in your case.
List<Transaction> findByAccbalance(long Accbalance);
but if you wanna use Query then can you check with below Query:--
#Query(value="select c from TRANSACTION c")
List<Transaction> findByAccbalance(long Accbalance);

Convert Long id to Set list using Mapstruct?

In a Mapstruct interface, how can I convert an id to a Set< UserSystem > ?
I tried as follows but unsuccessfully because error occurs:
#Mapper(componentModel = "spring", uses = {UserSystemService.class})
public interface CompanyPostMapper extends EntityMapper<CompanyPostDTO, Company> {
#Mapping(source = "userSystemId", target = "userSystems", expression = "java(userSystemService.findByIdAndAddToSet(id))")
Company toEntity(CompanyPostDTO dto);
default Company fromId(Long id) {
if (id == null) {
return null;
}
Company company = new Company();
company.setId(id);
return company;
}
}
I don't know if I understood the use of the "uses" parameter correctly, but basically I would like to get the ID and query the register and return a Set with the register.
I was going to try "qualifiedByName" and create a method in the Mapper interface, but I don't know how I can inject the Repository and I don't know if that would be a good practice.
What would be the best way to solve?
MapStruct is a mapping framework. You are doing a lookup inside the mapping. It is possible of course (look at the JPA mapping example where an #Context is used). But you can't inherit EntityMapper<CompanyPostDTO, Company> at the same time.
Normally, you need to take the lookup outside your mapping logic and use an update method to update the object.
Your mapping would then look like:
#Mapper(componentModel = "spring" )
public interface CompanyPostMapper {
void updateEntity(CompanyPostDTO dto, #MappingTarget Company entity);
}
// and your call would look like:
public class CallingService{
Company company = userSystemService.findByIdAndAddToSet(id));
if (company == null) {
company = new Company();
}
companyPostMapper.updateEntity(dto, company);
}

Spring Data Rest and collections with unique constraints

I'm evaluating spring-data-rest and am running into a situation where the magic no longer appears to be working in my favor.
Say I have a collection of items.
Parent - 1:M - Child
Parent
Long id
String foo
String bar
#OneToMany(...)
#JoinColumn(name = "parent_id", referencedColumnName = "id", nullable = false)
Collection<Child> items
setItems(items) {
this.items.clear();
this.items.addAll(items);
}
#Table(name = "items", uniqueConstraints = {#UniqueConstraint(columnNames = {"parent_id", "ordinal"})})
Child
Long id
String foo
Integer ordinal
The database has a constraint that children of the same parent can't have conflicting values in one particular field, 'ordinal'.
I want to PATCH to the parent entity, overwriting the collection of children. The problem comes with the default behavior of hibernate. Hibernate doesn't flush the changes from when the collection is cleared until after the new items are added. This violates the constraint, even though the eventual state will not.
Cannot insert duplicate key row in object 'schema.parent_items' with unique index 'ix_parent_items_id_ordinal'
I have tried mapping this constraint to the child entity by using #UniqueConstraints(), but this doesn't appear to change the behavior.
I am currently working around this by manually looking at the current items and updating the ones that would cause the constraint violation with the new values.
Am I missing something? This seems like a fairly common use case, but maybe I'm trying too hard to shoe-horn hibernate into a legacy database design. I'd love to be able to make things work against our current data without having to modify the schema.
I see that I can write a custom controller and service, à la https://github.com/olivergierke/spring-restbucks, and this would let me handle the entityManager and flush in between. The problem I see going that way is that it seems that I lose the entire benefit of using spring-data-rest in the first place, which solves 99% of my problems with almost no code. Is there somewhere that I can shim in a custom handler for this operation without rewriting all the other operations I get for free?
In order to customize Spring Data REST (my way to do, I have to speak about with Spring Data REST guys) like following:
Consider we have a exposed repository UserRepository on /users/, you should have at least the following API:
...
/users/{id} GET
/users/{id} DELETE
...
Now you want to override /users/{id} DELETE but keep other API to be handle by Spring Data REST.
The natural approach (again in my opinion) is to write your own UserController (and your custom UserService) like following:
#RestController
#RequestMapping("/users")
public class UserController {
#Inject
private UserService userService;
#ResponseStatus(value = HttpStatus.NO_CONTENT)
#RequestMapping(method = RequestMethod.DELETE, value = "/{user}")
public void delete(#Valid #PathVariable("user") User user) {
if (!user.isActive()) {
throw new UserNotFoundException(user);
}
user.setActive(false);
userService.save(user);
}
}
But by doing this, the following mapping /users will now be handle by org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping instead of org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping.
And if you pay attention on method handleNoMatch of org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping (parent of org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping) you can see the following thing:
else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
}
patternAndMethodMatches.isEmpty(): return TRUE if url and method (GET, POST, ...) does not match.
So if you are asking for /users/{id} GET it will be TRUE because GET only exists on Spring Data REST exposed repository controller.
!allowedMethods.isEmpty(): return TRUE if at least 1 method GET, POST or something else matches for the given url.
And again it's true for /users/{id} GET because /users/{id} DELETE exists.
So Spring will throw an HttpRequestMethodNotSupportedException.
In order to by-pass this problem I created my own HandlerMapping with the following logic:
The HandlerMapping has a list of HandlerMapping (here RequestMappingInfoHandlerMapping and RepositoryRestHandlerMapping)
The HandlerMapping loops over this list and delegate the request. If an exception occurs we keep it (we keep only the first exception in fact) and we continues to the other handler. At the end if all handlers of the list throw an exception we rethrow the first exception (previously keeped).
Moreover we implements org.springframework.core.Ordered in order to place the handler before org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* #author Thibaud Lepretre
*/
public class OrderedOverridingHandlerMapping implements HandlerMapping, Ordered {
private List<HandlerMapping> handlers;
public OrderedOverridingHandlerMapping(List<HandlerMapping> handlers) {
Assert.notNull(handlers);
this.handlers = handlers;
}
#Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Exception firstException = null;
for (HandlerMapping handler : handlers) {
try {
return handler.getHandler(request);
} catch (Exception e) {
if (firstException == null) {
firstException = e;
}
}
}
if (firstException != null) {
throw firstException;
}
return null;
}
#Override
public int getOrder() {
return -1;
}
}
Now let's create our bean
#Inject
#Bean
#ConditionalOnWebApplication
public HandlerMapping orderedOverridingHandlerMapping(HandlerMapping requestMappingHandlerMapping,
HandlerMapping repositoryExporterHandlerMapping) {
List<HandlerMapping> handlers = Arrays.asList(requestMappingHandlerMapping, repositoryExporterHandlerMapping);
return new OrderedOverridingHandlerMapping(handlers);
}
Et voilà.

How to define Spring Data Repository scope to Prototype?

I'm using Spring data jpa & hibernate for data access along with Spring boot. All the repository beans are singleton by default. I want to define the scope of all my repositories to Prototype. How can I do that?
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
Edit 1
The problem is related to domain object being shared in 2 different transactions which is causing my code to fail. I thought it is happening because repository beans are singleton. That's the reason I asked the question. Here is the detailed explanation of the scenario.
I have 2 entities User and UserSkill. User has 1-* relationship with UserSkills with lazy loading enabled on UserSkill relation.
In a UserAggregationService, I first make a call to fetch an individual user skill by id 123 which belongs to user with id 1.
public class UserAggregationService {
public List<Object> getAggregatedResults() {
resultList.add(userSkillService.getUserSkill(123));
//Throws Null Pointer Exception. See below for more details.
resultList.add(userService.get(1));
}
}
Implementation of UserSkillService method looks like
#Override
public UserSkillDTO getUserSkill(String id) {
UserSkill userSkill = userSkillService.get(id);
//Skills set to null avoid recursive DTO mapping. Dozer mapper is used
//for mapping.
userSkill.getUser().setSkills(null);
UserSkillDTO result = mapper.map(userSkill, UserSkillDTO.class);
return result;
}
In the call of user aggregation service, I call UserService to fetch userDetails. UserService code looks like
#Override
public UserDTO getById(String id) {
User user = userService.getByGuid(id);
List<UserSkillDTO> userSkillList = Lists.newArrayList();
//user.getSkills throws null pointer exception.
for (UserSkill uSkill : user.getSkills()) {
//Code emitted
}
....
//code removed for conciseness
return userDTO;
}
UserSkillService method implementation
public class UserSkillService {
#Override
#Transactional(propagation = Propagation.SUPPORTS)
public UserSkill get(String guid) throws PostNotFoundException {
UserSkill skill = userSkillRepository.findByGuid(guid);
if (skill == null) {
throw new SkillNotFoundException(guid);
}
return skill;
}
}
UserService method implementation:
public class UserService {
#Override
#Transactional(readOnly = true)
public User getByGuid(String guid) throws UserNotFoundException {
User user = userRepo.findByGuid(guid);
if (user == null) {
throw new UserNotFoundException(guid);
}
return user;
}
}
Spring boot auto configuration is used to instantiate entity manager factory and transaction manager. In the configuration file spring.jpa.* keys are used to connect to the database.
If I comment the below line of code, then I do not get the exception. I am unable to understand why change in the domain object is being affecting the object fetch in a different transaction.
userSkill.getUser().setSkills(null);
Please suggest If I have missed something.

Resources