Java 如何避免在服务类中重复使用DAO方法@事务性带注释的DAO和服务类-这是可接受的实践吗?

Java 如何避免在服务类中重复使用DAO方法@事务性带注释的DAO和服务类-这是可接受的实践吗?,java,spring,transactions,annotations,dao,Java,Spring,Transactions,Annotations,Dao,我知道最佳实践是同时拥有服务层和dao层,并在服务级别上添加@Transactional注释。但在我的例子中,这意味着我创建的大多数服务类只是为了重复DAO方法。。。这很烦人 例如 公共接口FooDAO{ 公共列表(int cathegoryId); 公共列表(int cathegoryId、int ownerId); } @服务 @交易的 公共类餐饮服务{ 受保护@Autowired FooDAO; 公共列表(int cathegoryId){ dao.list(cathegoryId); }

我知道最佳实践是同时拥有服务层和dao层,并在服务级别上添加@Transactional注释。但在我的例子中,这意味着我创建的大多数服务类只是为了重复DAO方法。。。这很烦人

例如

公共接口FooDAO{ 公共列表(int cathegoryId); 公共列表(int cathegoryId、int ownerId); } @服务 @交易的 公共类餐饮服务{ 受保护@Autowired FooDAO; 公共列表(int cathegoryId){ dao.list(cathegoryId); } 公共列表列表(int cathegoryId,int authorId){ dao.list(cathegoryId、authorId) } } 这有多蠢

在大多数情况下,我真的不需要花哨的服务方法,因为通常这是一个获取例如cathegory描述和匹配cathegory的实体列表的问题。这就是为什么我在寻找一个简化的解决方案。像使用泛型避免重复DAOs:D一样出色的东西

我一直在寻找答案。我读过很多书 但仍然没有找到我的答案

所以我想知道用@Transactional注释DAO方法是否真的是个坏主意。受到启发,我想出了一个解决办法

如果:

  • 我只有一个服务类(这是真正需要的),并用@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";
    }
}
公共接口FooDAO{ @事务性(传播=propagation.MANDATORY,只读=true) 公共列表(int cathegoryId); ... } @服务 公共类餐饮服务{ 受保护@Autowired FooDAO; @需要事务//传播 公共列表魔术(FooVO FooVO){ //在这里做复杂的事情;) } //我们不在服务类中重复DAO方法。 //这里没有包装方法!!! } @控制器 公共类FoodMagicController{ 受保护@Autowired FooService; ... fooService.magic(fooVO); ... } @控制器 公共类FooController{ 受保护的@Autowired FooDAO;//直接在控制器类中连接的dao!!! @事务性(传播=传播。需要\u新建) @请求映射(“…”) 公共字符串listFoo(模型,…)引发异常{ model.addAttribute(“list”,dao.list(13)); 返回“xyz”; } } 在每种情况下,DAO都使用“上面”管理的会话


这是个坏主意吗?有没有更好的方法来实现我的需求?

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

如果您觉得不需要任何服务类(即,带有API的类比纯DAO 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)
       }

}
公共类HomePageController{
@自动连线
私人录像服务;
//控制器方法
@请求映射(value=“/tag/mostView”)
公共@ResponseBody
地图显示MOSTVIEWEDVIDEOSFORTAG(){
//服务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);
}
}
@Service(value=“videoService”)
@事务(只读=真)
公共类VideoServiceImpl实现了VideoService{
@自动连线
私人视频刀;
@自动连线
私人塔道塔道;
//DAO的包装器API
@凌驾
公共列表getMostViewedVideo(字符串标记名){
返回videoDAO.getmostvieweddevideo(标记名);
}
//执行某些业务逻辑的非包装器API
@凌驾
@交易的
public void assignTagsToVideo(字符串videoId、字符串userId、字符串…标记){
用于(字符串标记:标记){
if(tagHeritageDAO.getTagHeritage(tag、videoId、userId)==null){
Tag tagObj=tagDAO.getTag(Tag);
if(tagObj!=null){
//这里有一些向视频添加标签的逻辑
}
}
}
更新(视频);
}
}

正如您所看到的,唯一的服务连接到控制器类,dao连接到服务类。这就是我所说的混合模式。如果我把你弄糊涂了,很抱歉。

是的,最后一段描述了我的情况。所以你说将事务性服务与trnsAction DAO混合使用毕竟不是一个坏做法,对吗?而且,既然你没有给我任何其他建议,也许是这种情况下的最佳解决方案,据我所知?是的。我在混合模式下使用它,因此我的控制器只与服务类对话,而不与dao对话。这是更好的逻辑分离。等等,混合模式对我来说意味着其他东西。这意味着有些控制器使用服务,有些使用DAO。我知道这可能会让人困惑等(我是指那些使用我的代码的人),但在我的情况下,这是有道理的
@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);
}
}