如何避免在Service类中重复使用DAO方法?@Transactional DAO和Service类-这是可接受的实践吗?

10

我知道最佳实践是同时拥有服务层和数据访问层,并在服务层添加@Transactional注解。但在我的情况下,这意味着大部分服务类只是为了重复DAO方法而创建... 这相当令人烦恼。

例如:

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) 
   }
}

这是多么蠢啊?

在大多数情况下,我真的不需要繁琐的服务方法,因为通常只涉及获取例如类别描述和与类别匹配的实体列表。这就是为什么我正在寻找简化的解决方案。像使用泛型来避免重复DAO一样聪明 :D http://www.javablog.fr/javahibernate-dont-repeat-the-dao-with-a-genericdao.html

我已经搜索了答案。其中包括我阅读过的其他问题,但仍然没有找到我的答案。

所以我想知道,用@Transactional注释DAO方法是否真的是一个坏主意。受http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/#apistrategy的启发,我想出了一个解决方案。

如果:

  • 我只有一个服务类(真的需要),并对其方法进行@Transaction注释
  • 对于所有其他(简单)情况:我使用@Transactional(propagation = Propagation.MANDATORY)注释DAO方法,然后在我的控制器方法中使用@Transactional(propagation = Propagation.REQUIRES_NEW)

** 更新1 **

它可能看起来像这样:

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";
    }
}

在每种情况下,DAO都使用由上层管理的会话。

这是一个很糟糕的想法吗?有没有更好的方法来实现我需要的功能?


我找到了类似的问题:https://dev59.com/TW855IYBdhLWcg3wPBpx “是的,在您的控制器方法中添加@Transactional是完全有效的。” 但是,“控制器不应该知道持久层,而且您可能需要在桌面应用程序中重用业务逻辑,其中您的控制器层不再存在..” - Patrycja K
1个回答

2
我不认为这是一个坏主意,因为它取决于您选择设计应用程序的情况。如果您觉得不需要任何服务类(即具有超出纯DAO API的API的类),那么最好避免使用服务类,直接使用自动连接到控制器的DAO实现即可。但是,如果您需要执行一些额外的逻辑并将其公开为API,则可以编写服务类,该类将实现该自定义逻辑以及那些DAO方法的包装器函数(如上所述)。这将使代码更清晰,因为您只需要将服务类连接到控制器,同时还可以通过在服务类中使用包装器API来进行DAO调用。如果您仅将服务类保留为自定义API而没有任何DAO的包装器API,则如果需要进行任何数据访问调用,则还需要将DAO连接到您的控制器类中。因此,在这种情况下,实际上您将在服务类和控制器类中连接DAO。 更新1 以下是我从其中一个示例项目中提取的控制器和服务类。
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(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);
}
}

正如您所看到的,唯一的服务是在控制器类中有线连接的,而dao则连接到服务类中。这就是我所说的混合模式。如果我让您感到困惑,请谅解。


是的,最后一段描述了我的情况。所以你的意思是混合事务性服务和事务性DAO并不是一个坏习惯,对吗?而且,既然你没有给我任何其他建议,在这种情况下可能是最好的解决方案,我理解的对吗? - Patrycja K
是的。我在混合模式下使用它,这样我的控制器只与服务类交互而不与DAO交互。这是更好的逻辑分离。 - Dhanush Gopinath
1
等等,对我来说混合模式意味着另外一件事。它意味着一些控制器使用服务,一些使用DAO。我知道这可能会让人感到困惑等等(我的意思是那些与我的代码一起工作的人),但在我的情况下,这是有意义的。 - Patrycja K
我已经更新了答案,并提供了一段代码示例。希望这有助于理解我的意思。但是,如果您认为注入到控制器中的那些DAO不需要任何包装服务,那也完全可以。 - Dhanush Gopinath
1
是的Dhanush,你的例子展示了一个经典案例。“混合模式”是指控制器直接使用DAO。代码大致如下: public class HomePageController { private @Autowired FooDAO fooDAO; private @Autowired VideoService videoService; ... } - Patrycja K
我还更新了我的问题并包含了一小段代码片段,这样就没有疑问我在谈论什么了 :) - Patrycja K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接