Announcement Announcement Module
Collapse
No announcement yet.
PageRequests in ThreadLocal Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • PageRequests in ThreadLocal

    So, in another thread I posted that it would be nice to not have to have our api interface methods take a PageRequest, because in a Service interface we are tying UI or Query code into our Business Logic Service class. So I created one. Here is an example and the code for my ThreadLocal PageRequest.

    Example, in my Controller, I get two path variables. One for page and one for size of page. I start there with my ThreadLocal

    Code:
        @RequestMapping(value="/friends/{page}/{size}", method = RequestMethod.GET)
        @ResponseStatus(HttpStatus.OK)
        public @ResponseBody ListOfDomains<User> getFriendsOfUser(@PathVariable("page") int page,
                                                                  @PathVariable("size") int size) {
            PageRequestHandler.setPageRequest(page, size);
            List<User> allUsers = userService.getFriends(userService.getUserFromSession());
            return new ListOfDomains<User>(allUsers, UserDivsTitlesAndRelationships.FRIENDS);
        }
    This line PageRequestHandler.setPageRequest(page, size); creates a PageRequest object and puts it into a ThreadLocal variable.

    To get that PageRequest in my Service class, so I can pass it to my Spring Data Repo that takes a PageRequest as a param is

    Code:
    public interface UserService {
        
        List<User> getFriends(User user);
    Code:
    @Override
        public List<User> getFriends(User user) {
            return userRepository.findFriends(user, PageRequestHandler.getPageRequest())
                                .getContent();
        }
    So PageRequestHandler.getPageRequest() gets me my PageRequest I put into ThreadLocal in my Controller.

    Here is the interface method in the Repo

    Code:
    @Query("start user=node({0}) " +
               "match (user)-[:FRIEND]-(friends) " +
               "return friends " +
               "order by friends.lastName asc")
        public Page<User> findFriends(User user, Pageable page);
    Here is my PageRequestHandler class. It is not 100% complete for all variations, including sort and properties, but it is a start.

    Code:
    public class PageRequestHandler {
        
        private static ThreadLocal<PageRequest> pageRequestThreadLocal = new ThreadLocal<PageRequest>();
        private static int defaultPageSize = 200;
        private static int defaultPage = 0;
        
        public static void setPageRequest(int page, int size) {
            PageRequest pageRequest = new PageRequest(page, size);
            pageRequestThreadLocal.set(pageRequest);
        }
        
        public static void setPageRequest(PageRequest pageRequest) {
            pageRequestThreadLocal.set(pageRequest);
        }
        
        public static PageRequest getPageRequest() {
            PageRequest pageRequest = pageRequestThreadLocal.get();
            if (pageRequest == null) {
                pageRequest = new PageRequest(defaultPage, defaultPageSize);
            }
            setPageRequest(pageRequest);
            return pageRequest;
        }
        
        public static void setDefaultPage(int page) {
            defaultPage = page;
        }
    
        public static void setDefaultPageSize(int pageSize) {
            pageSize = pageSize;
        }
    }
    Hope it helps some people.

    Mark
    Last edited by bytor99999; Feb 5th, 2012, 01:58 PM. Reason: add repo method

  • #2
    What's the reason you prefer this way over simply changing the method signature of the service to take a Pageable and return a page? The way you have it right now is much harder to understand as the interface cannot be strictly defined semantically. Either you'd assume to get all Users back or you'd pretty much have to imply the implementation already as - if you defined the method as returning a slice of users - the implementation had to lookup the Pageable by some magic (read the ThreadLocal) means.

    I'd argue that the service interface is the top-level interface for programatic access to your business code. Thus it's good practice to make it as little surprising as possible.

    Comment


    • #3
      "I'd argue that the service interface is the top-level interface for programatic access to your business code."

      Exactly, so anytime you make the interface api take parameters for a particular technology, you couple your api/code to that technology. But Spring promotes loose couple. If my Service interface takes a PageRequest or returns a Page object, then what happens if I change my database, repository, or what if I have a different client that doesn't want Pagination. Then I have to either create a new interface and new implementation, or add more methods to my interface to support both scenarios. Which leads to more code, which leads to more maintenance, and therefore more costs.

      That is the driving factor in all my code, maintenance, and I see having PageRequest/Page in my Service api as a risk to higher maintenance.

      With my approach, I can have any implementation of the Service interface, with paging or without, without changing the api, which changing the api has more rippling effects.

      Mark

      Comment


      • #4
        To give you an example where Spring does this is with JdbcTemplate and a Connection object. I would never ever want my repo method signatures to take a Connection as a parameter, that definitely couples your code to Jdbc, and if you change to any ORM, you have a lot of code to fix.

        Thanks

        Mark

        Comment

        Working...
        X