10

我知道最佳实践是同时拥有服务和 dao 层,并在服务级别添加 @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 注释属于哪里? 但仍然没有找到我的答案。

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

如果:

  • 我只有一个服务类(这是真正需要的)并用 @Transactional 注释它的方法
  • 对于所有其他(简单)情况:我用 @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 都使用“上方”管理的会话。

这是一个非常糟糕的主意吗?有没有更好的方法来实现我所需要的?

4

1 回答 1

2

我不会说这是一个坏主意,因为这取决于您选择设计应用程序的情况。

如果您觉得,您不需要任何服务类(即具有比纯 DAO API 更多功能的 API 的类),那么我认为最好避免使用服务类,而直接使用自动连接到控制器的 DAO 实现。

但是,如果您需要执行一些额外的逻辑并希望将其公开为 API,那么您可以编写服务类,它将实现该自定义逻辑以及这些 DAO 方法的包装函数(就像您在上面给出的那样)。这将使代码更简洁,因为您只需要将服务类连接到控制器中,同时您可以使用服务类中的包装 API 进行 DAO 调用。

如果您仅为自定义 API 保留服务类并且没有任何 DAO 的包装 API,那么如果您需要进行任何数据访问调用,您还需要将 DAO 连接到您的控制器类中。因此,在这种情况下,您将有效地在服务类和控制器类中连接 DAO。

更新 1

这是来自示例项目之一的我的 Controller 和 Service 类

控制器

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 连接到服务类。这就是我所说的混合模式。抱歉,如果我让你感到困惑。

于 2013-04-22T12:58:32.103 回答