I've managed to get Spring Data JPA up and running with my current project. Everything has been going fairly smooth up until now.
I was wondering, is there a way to handle Spring Data Repository responses inside the actual Repository/Base repository implementation? (Or perhaps in an interceptor?)
For instance: I have the need to check if any results were returned from my repository methods, and throw an exception if not like so:
(Preferably keeping the 'magic' of Spring data and not having to implement every repository method)
public interface MyRepository<T,ID> extends CrudRepository<T,ID> {
List<T> findByName(String name);
}
In this case I'd want to throw an exception if the resulting List<T> was null/empty.
Example 2: I have the need to check permission to read, to make sure I have the right to read the requested object from the database. Same thing goes here, I want to inspect / intercept the repository result and find out if the entity instance is the right one considering my context. (For security reasons)
TL;DR Is there any way of "intercept"/inspect the resulting entities/list from a repository method without having to implement ALL magic findBy... methods from all repositories? Perhaps in the base repository implementation or via an interceptor of some kind?
Regards
EDIT I have seen the answer in this particular SO question and I was wondering if there's another way of doing it, which doesn't involve the use of a custom implemented MethodInterceptor?
Regards
Related
I created a custom repository interface that extended by CrudRepository.
I just used #EnableJdbcRepositories in StartApplication.
When I test a method from my custom repository, such as save(T t) I seen it be instanced by SimpleJdbcRespository.class.
I interested it, I want to know how and where was the SimpleJdbcRespository created and have implemented my custom repository interface.
Which line of code should I debug?
All Spring Data JDBC repositories get created by JdbcRepositoryFactory which uses SimpleJdbcRepository as the implementation for the CrudRepository methods.
For additional methods in the repository interface JdbcQueryLookupStrategy is consulted which determines how to get the right query for a method to execute in the form of a RepositoryQuery.
Currently there is just implementation of that: JdbcRepositoryQuery
If something doesn't work as expected with the execution of such a method. JdbcRepositoryQuery.execute(Object[] objects) is a good start for debugging.
You probably also want to look up how the executor used in there get constructed.
I have a folder /controllers with controllers and another /models where i put my models with get/sets.
Now, where is the usual place to put the methods to get data from the db,etc...?
In my last project I put it under the get/sets and it was a bit confusing.
Any helP?
For database you can have a DTO folder, but you could also have a intermediate Service folder where you put all the business rules. I like having a separate DTO object to make mocking easier too.
For strictly data access, spring boot handles most of CRUD. You just need to config spring to know you have a data access layer. #JpaRepository does this. This is probably where you should start looking. For custom data calls you're going to have to write custom methods inside the classes that make up the data access laters.
Your question is extremely broad but I hope I've given you enough to look at.
https://spring.io/guides/gs/accessing-data-jpa/
I am developing a spring application, where I have three layers as most of other spring apps. The Rest Controllers on front, Services in middle, and JPA repositories in behind. Now we have spring entities mapped to the db, in my case they are plain old java objects(POJO), with only some fields and getters and setters which I usually prefer and don't want to put any business logic in there. However, in this project, I find out that in a lot of services I am repeating the same piece of code, something like that
User user=userRepository.findUserByName("some name here");
if(user==null){
throw new UserNotFoundException("User not found");
}
Now, this is not only for a single entity, there are many other similar repeated parts too. So, I have started to worry about it and looking possible areas to push that code and eliminate the repeated parts. One thing makes sens as stated in domain driven design, put that business logic inside the entity, now they will have both data and part of business logic. Is that a common practice?
Pretty much looks like a simple code reuse problem. If you are always throwing the same exception in all contexts then what about implementing a findExistingUserByName method on the repository that throws if the user doesn't exist?
Your code would become:
User user = userRepository.findExistingUserByName("username");
If you do not want to change the repository contract you could also implement a UserFinderService at the application level which wraps over a UserRepository and provides that service-level behavior.
Another more generic idea could be to implement a generic method and make it available to your application services either by inheritance, composition or a static class which would allow you to do something like:
withExistingAggregate<User>(userRepository.findUserByName("username"), (User user) -> ...)
You cat return Optional<User> from repository in this and similar cases.
Then you service code will look like:
userRepository.findUserByName("some name here")
.ifPresent(user -> doThmsWithUser(user));
I'm learning AOP and am comfortable with Pointcuts, Advices etc.
What am going to ask, am pretty sure is not possible, but want to ask anyways.
I have a method which takes a userId, fetches the user's record from a database and then does something to the record. I have like twenty different methods that do different things, but all of them take the userId as input and fetch the record from database. This to me looks like a cross cutting concern that can be pulled into an aspect.
But how? I know I can access the arguments (userId in this case), access the return value of the method and catch the methods exception. But how do I give the method something to work with (record in the database in this case?)
public String printUserDetails(String userId)
{
Record record = Database.fetchRecord(userId);
System.out.println(record.getDetails());
return record.getTitle();
}
So, is there a way to pull that database accessing code into an aspect?
One way I can think of is declare something like the following for input
class RequestObject
{
String userId;
Record record;
}
and inject the record in the Aspect and then call proceed(). But this somehow feels wrong.
IMO, resolving a user, using the userid, is not a cross-cutting concern and hence aspect is not the right way. The first landing page that receives a userId should actually resolve it to UserRecord and from then on, the userRecord should be the one moving around in the application.
A simple analogy I can draw to your scenario from one of my applications is, all authenticated servlets expect the servletRequest.getRemoteUser() to return the valid user login corresponding to the user sending the request. We decorated the HttpServletRequest to resolve this to a User object in our application and all the authenticated servlets downcast the HttpServletRequest to AuthenticatedServletRequest and extract this object. No one else within the application tries to resolve a user login anymore.
You cannot access a method's local variables from AspectJ if this is what you wanted to know.
The rest of the question is rather about design and the answer dependent on what you want to achieve. You can avoid code duplication in multiple methods using a template method design pattern. You can inject real or mock objects into classes if you refactor them to have a member instead of local variables. It is another question if you create the member by directly refactoring your classes or via AspectJ's (ITD)[http://www.eclipse.org/aspectj/doc/next/progguide/starting-aspectj.html#inter-type-declarations] mechanism. A third question would be if you possibly want to use an aspect for caching in order to avoid fetching the same object from the database multiple times.
I am not sure what exactly you want to achieve, so I cannot answer more specifically.
In a web application that uses Spring Data JPA with Hibernate, we utilize the web pagination functionality to provide paging and sorting capabilities in various lists of entities.
#Controller
public class MyEntityController {
#RequestMapping(method = RequestMethod.GET)
public ModelAndView list(Pageable pageable) { ... }
}
#Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
argumentResolvers.add(new PageableArgumentResolver());
}
}
public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, String> {
Page<MyEntity> findByPropertyX(String propertyX, Pageable pagable);
}
This allows for entity properties to be defined in the rendered html as special sort request parameters, where the page.sort value actually matches a property in the entity upon which to sort.
<table>
<thead>
<tr>
<th>Property X</th>
<th>Property Y</th>
</tr>
</thead>
<tbody>...</tbody>
</table>
This produces a resulting URL such as:
http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc
The problem is that users may modify the URL to use invalid page.sort properties that either reference non-existent column/property names, or worse, that use invalid JPA query characters that result in invalid syntax.
For example, if the URL is modified to sort on "noSuchProperty":
http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc
But this property doesn't exist, the following exception will be thrown:
java.lang.IllegalArgumentException: No property noSuchProperty found for type class com.my.company.MyEntity
at org.springframework.data.repository.query.parser.Property.<init>(Property.java:76)
. . .
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:86)
. . .
at $Proxy68.findByPropertyX(Unknown Source)
at com.my.company.MyEntityRepository.findByPropertyX(MyEntityRepository.java:17
Likewise, if the URL is modified to an invalid query syntax character, such as """:
http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc
The following error will occur:
java.lang.StackOverflowError
java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
. . .
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
(There is also a third flavor of exceptions which results in a org.hibernate.QueryException when the #Query is explicitly defined on the Repository method.)
Spring Data JPA abstracts away the details of the sorting, paging, and handling of these parameters; however, it doesn't seem to gracefully handle these scenarios (i.e. where an invalid sort parameter is specified).
We could add in some additional custom logic to validate that the sort property actually exists on the entity; however, I'm wondering if there is a cleaner more centralized approach for doing this such that we don't lose the benefits and simplicity of the Spring Data JPA abstractions. We use this sorting capability throughout our app with many different entities, so ideally, we'd want more of a generic approach, rather than having to explicitly define or check the sort properties for every entity page requested.
Specifically, we actually extend the PageableArgumentResolver to accept an annotated sort default value that is provided in our controller (not illustrated in the code examples for simplicity), so we'd like to just fallback to this default sort order, or just the default sorting order for the entity, rather than throwing an exception.
Some ideas and attempts.. I could use a QueryCreationListener to intercept the query creation and get the sort parameter; however, I can't actually modify the query at that point. Or, I can extend and use a custom PageableArgumentResolver (we are already doing this) to grab the sort parameters; however, I don't have access to the entity at that point, nor the ability to determine if the entity actually has a property by that name. We could declare the supported properties explicitly; however, again, this defeats the idea of centrally and automatically handling this scenario without requiring specific or declared knowledge of the entities.
Is there any other type of interceptor or similar construct that I can utilize to centrally validate pageable sort parameters and modify if necessary before invoking the query? Or is there any type of configuration or way that Spring can automatically handle this scenario such that it more gracefully handles invalid sort params?
I was taking a look at the code and I think some more of the stack trace would be helpful. But from what I can see, I think there are two places you might want to tackle if you are in the mood to rewrite some Spring code.
There are two scenarios here, in the first one you are passing a sort field that doesn't exist in the object/table. What you really want is for that bad parameter to be silently ignored all the time, not just when passing in a 1PageableArgumentResolver]1. I'm thinking it should be an option on the AbstractQueryCreator (and hence, the JpaQueryCreator) to ignore bad parameters on a sort.
The second part that should be tackled is probably the PageableArgumentResolver. If you pass empty strings or something that doesn't make sense like %20 then it should ignore that parameter and not send it through to the PageRequest.
Happy hacking and good luck. Reading your post has made me realize that my site is vulnerable to the same problem and I really have no good solution.
I think improving the PageableArgumentResolver to gracefully handle those scenarios. It could try to create a PropertyPath instance from the String handed in as Sort and thus make sure it's a valid one. I am a bit torn on whether it makes sense to simply drop the invalid String by default, which would return the result not being sorted at all. This is probably the most seamless experience but might also lead to tedious attempts to find out why the result is not sorted.
However, it. Would be cool if you could raise a JIRA ticket for that against Spring Data Commons and just link this ticket here. Feel free to open a pull request in case you have fleshed out a viable implementation. Thanks for bringing this to the table already!