10

I know that the best practice is having both service and dao layers and to add @Transactional annotations on the service level. But in my case it means most of my service classes is created just to repeat DAO methods... It is quite irritating.

eg.

public interface FooDAO {
 public List<FooVO> list(int cathegoryId);
 public List<FooVO> list(int cathegoryId, int ownerId);
}

@Service
@Transactional
public class FooService {
   protected @Autowired FooDAO dao;
   public List<FooVO> list(int cathegoryId) { 
      dao.list(cathegoryId); 
   }
   public List<FooVO> list(int cathegoryId, int authorId) { 
      dao.list(cathegoryId, authorId) 
   }
}

How dumb is that?

In most cases I really don't need fancy service methods since usually it is a matter of geting eg. a cathegory description and a list of entities that match the cathegory. That's why I'm looking for a simplified solution. Something as brilliant as using generics to avoid repeating DAOs :D http://www.javablog.fr/javahibernate-dont-repeat-the-dao-with-a-genericdao.html

I've searched for the answer. Among others I've read Where does the @Transactional annotation belong? but still haven't found my answer.

So I wonder if annotating DAO methods with @Transactional is really such a bad idea. Inspired by http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/#apistrategy I figured out a solution.

WHAT IF:

  • I have only one service class (which is really needed) and annotate its methods with @Transactional
  • for all other (simple) cases: I annotate DAO methods with @Transactional(propagation = Propagation.MANDATORY) and my controller methods with @Transactional(propagation = Propagation.REQUIRES_NEW)

** UPDATE 1 **

It would look something like this:

public interface FooDAO {
 @Transactional(propagation = Propagation.MANDATORY, readOnly=true)
 public List<FooVO> list(int cathegoryId);
 ...
}

@Service
public class FooService {
   protected @Autowired FooDAO dao;

   @Transactional // propagation REQUIRED
   public List<FooVO> magic(FooVO fooVO) { 
      //do sth complicated here ;)
   }
   // We do not repeat DAO methods in the Service class. 
   // No wrapping methods here !!!
}

@Controller
public class FooMagicController {
    protected @Autowired  FooService fooService;
    ...
        fooService.magic(fooVO);
    ...
}
@Controller
public class FooController {
    protected @Autowired  FooDAO dao; //DAO wired directly in the Controller class !!!

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @RequestMapping(".....")
    public String listFoo(Model model,...) throws Exception {
        model.addAttribute("list", dao.list(13) );
        return "xyz";
    }
}

In each case DAO uses session which is managed "above".

Is it very bad idea? Is there any better way to achieve what I need?

Community
  • 1
  • 1
Patrycja K
  • 731
  • 7
  • 14
  • I've found similar question: http://stackoverflow.com/questions/4462785/transactional-controller-vs-service "yes, adding @Transactional to your controller methods is perfectly valid." but "a controller shouldn't be aware of the persistence layer, and you could have to reuse your business logic in a desktop application where your controller layer no longer exists.." – Patrycja K Apr 24 '13 at 06:57

1 Answers1

2

I wouldn't say it is a bad idea, as it depends on the situation you chose to design the application.

If you feel, you do not need any service classes (i.e. classes with API's that does more than pure DAO API) then I feel it is better to avoid service classes and just use the DAO implementations directly auto wired into the controller.

But if you need to do some additional logic and want to expose it as an API, then you can write the service class, which will implement that custom logic and also the wrapper functions for those DAO methods (like you gave above). This will make the code cleaner as you only need to wire the service classes into the controller and at the same time you can make the DAO calls by using the wrapper API's in the service classes.

If you keep service classes only for custom API's and not have any wrapper API's of DAO's then you would also need to wire DAO's into your controller class, if you need to make any data access calls. So in this case effectively you will be wiring DAO's in Service classes and Controller classes.

UPDATE 1

Here are my Controller and Service classes from one of the sample projects

Controller

public class HomePageController  {


@Autowired
private VideoService videoService;

    //Controller method
@RequestMapping(value = "/tag/mostviewed")
public @ResponseBody
Map<String, List<Video>> showMostViewedVideosForTag (){
            //service api
             videoService.getMostViewedVideo(curatorTagName)
       }

}

Service class

@Service(value = "videoService")
@Transactional(readOnly = true)
public class VideoServiceImpl implements VideoService {

@Autowired
private VideoDAO videoDAO;

@Autowired
private TagDAO tagDAO;

 // WRAPPER API FOR THE DAO

@Override
public List<Video> getMostViewedVideo(String tagName)  {
    return videoDAO.getMostViewedVideo(tagName);
}


 // A non wrapper API which does some business logic
@Override
@Transactional
public void assignTagsToVideo(String videoId, String userId, String... tags)  {

        for (String tag : tags) {
            if (tagHeritageDAO.getTagHeritage(tag, videoId, userId) == null) {
                Tag tagObj = tagDAO.getTag(tag);
                if (tagObj != null) {
                    //some logic here to add tags to video
                }
            }
        }

    videoDAO.update(video);
}
}

As you see the only service is wired in the controller class, and dao's are wired to the service class. This is what I meant by mixed mode. Apologies if I confused you.

Dhanush Gopinath
  • 5,652
  • 6
  • 37
  • 68
  • Yup, the last paragraph describes my case. So you say mixing transactional service with trnsactional DAO is not a bad practice after all, right? And, since you didn't advise me anything else, probably the best solution in this situation, as I understand? – Patrycja K Apr 22 '13 at 14:32
  • Yes. I use it in the mixed mode, so that my controller only talks to the service classes and not to the dao's. That is better separation of logic. – Dhanush Gopinath Apr 22 '13 at 14:46
  • 1
    Wait wait, mixed mode means something else for me. It means some controllers use services and some use DAOs. I know it may be confusing etc. (I mean for those who work with my code) but in my case it makes sense. – Patrycja K Apr 22 '13 at 15:57
  • I have updated the answer with a sample snippet of code. Hope it helps to understand what I meant. But if you feel those DAO's that are injected into controllers do not require any wrapper services, then that is perfectly fine. – Dhanush Gopinath Apr 22 '13 at 17:02
  • 1
    Yup Dhanush, your example shows a classic case. "Mixed mode" would be when Controller uses DAO directly. It would be something like this: ` public class HomePageController { private @Autowired FooDAO fooDAO; private @Autowired VideoService videoService; ... } ` – Patrycja K Apr 23 '13 at 11:05
  • I have also updated my question and included a snippet. So that there is no doubt what I'm talking about :) – Patrycja K Apr 23 '13 at 11:34