Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid repeatig DAO methods in Service classes? @Transactional annotated DAO and Service classes - is it acceptable practice?

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?

like image 215
Patrycja K Avatar asked Apr 22 '13 12:04

Patrycja K


People also ask

When should we use @transactional annotation?

The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic. In the declarative approach, we annotate the methods with the @Transactional annotation.

What is use of @transactional why it should be used on service layer?

You should use @Transactional at service layer, if you want to change the domain model for client B where you have to provide the same data in a different model,you can change the domain model without impacting the DAO layer by providing a different service or by creating a interface and implementing the interface in ...

Can we use @transactional in service layer?

The @Transactional annotation belongs to the Service layer because it is the Service layer's responsibility to define the transaction boundaries.

Does Dao have to be transactional?

PRO: DAO will find connection in context and will not care on transaction boundaries.


1 Answers

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.

like image 66
Dhanush Gopinath Avatar answered Oct 23 '22 12:10

Dhanush Gopinath