springmvc事务性dao服务层和控制器层

9rnv2umw  于 2021-06-30  发布在  Java
关注(0)|答案(1)|浏览(357)

我将spring mvc用于spring数据。
我的问题的简单例子:
我的dao服务类:

@Service
@AllArgsConstructor
@Transactional
public class FooService{
    private FooRepository fooRepo;

    public Foo save(Foo foo){
        return fooRepo.save(foo);
    }
}

和控制器:

@Controller
@AllArgsConstructor
@Transactional //if I remove this, method add does not save a foo. 
        //But I don't understand why, because FooService already has @Transactional annotation
public class FooController{

    private FooService fooService;

    @PostMapping("/add")
    public String add(@RequestParam("programName") String programName, @RequestParam("id") long id){
        Foo foo = fooService.findById(id).get();
        foo.setProgramName(programName);
        fooService.save(foo);
        return "somePage";
    }
}

如果我从控制器类中删除@transaction注解,方法save将不会更新foo对象。我不明白为什么我应该用@transactional注解来标记controller,如果我已经用这个注解来标记服务类了?

############ 更新####################

简单详细说明:
我有项目和教育实体。一个程序有多个教育,教育实体有外键程序id。有一个带有程序窗体的页面,有字段:程序id、程序主题,…,以及带有教育id列表(用逗号分隔)的字段。
我正在尝试更新程序中的教育列表,因此我在页面窗体中添加了一个新的教育id,然后单击“保存”。通过调试器我看到,新的教育已经出现在程序中,但更改不会出现在数据库中。

@Controller
@RequestMapping("/admin/program")
@AllArgsConstructor //this is lombok, all services autowired by lombok with through constructor parameters
@Transactional//if I remove this, method add does not save a foo. 
        //But I don't understand why, because FooService already has @Transactional annotation
public class AdminProgramController {

    private final ProgramService programService;
    private final EducationService educationService;
    @PostMapping("/add")
    public String add(@RequestParam("themeName") String themeName, @RequestParam("orderIndex") int orderIndex,
                                  @RequestParam(value = "educationList", defaultValue = "") String educationList,
                      @RequestParam(value = "practicalTestId") long practicalTestId){

        saveProgram(themeName, orderIndex, educationList, practicalTestId);
        return "adminProgramAdd";

    private Program saveProgram(long programId, String themeName, int orderIndex, String educationList, long practicalTestId){

        List<Long> longEducationList = Util.longParseEducationList(parsedEducationList); //this is list of Education id separeted by commas that I load from page form
        //creating new program and set data from page form
        Program program = new Program();
        program.setId(programId);
        program.setThemeName(themeName);
        program.setOrderIndex(orderIndex);

        //starting loop by education id list
        longEducationList.stream()
                .map(educationRepo::findById)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .forEach(edu->{
                    //linking program and education
                    program.getEducationList().add(edu);
                    edu.setProgram(program);
                });

        //saving new program or updating by service if there is one already
        Program savedProgram = programService.save(program);
        //saving education with updated program
        for(Education edu : savedProgram.getEducationList())
        {
            educationService.save(edu);
        }

        return savedProgram;
    }
}

程序服务:

@Service
@AllArgsConstructor //this is lombok, all services autowired by lombok with throught constructor parameters
@Transactional
public class ProgramService {
    private ProgramRepo programRepo;

    //other code here.....

    public Program save(Program program) {
        Optional<Program> programOpt = programRepo.findById(program.getId());

        //checking if the program is already exist, then update it paramateres
        if(programOpt.isPresent()){
            Program prgm = programOpt.get();
            prgm.setThemeName(program.getThemeName());
            prgm.setOrderIndex(program.getOrderIndex());
            prgm.setPracticalTest(program.getPracticalTest());
            prgm.setEducationList(program.getEducationList());
            return programRepo.save(prgm);
        }
        //if not exist then just save new program
        else{
            return programRepo.save(program);
        }
    }
}

教育服务

@Service
@AllArgsConstructor //this is lombok, all services autowired by lombok with throught constructor parameters
@Transactional
public class EducationService {
    private EducationRepo educationRepo;

    //other code here....

    public Education save(Education education){
        return educationRepo.save(education);
    }

}

项目实体:

@Entity
@ToString(exclude = {"myUserList", "educationList", "practicalTest"})
@Getter
@Setter
@NoArgsConstructor
public class Program implements Comparable<Program>{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Column(name = "theme_name")
    private String themeName;
    @Column(name = "order_index")
    private int orderIndex; //from 1 to infinity

    @OneToMany(mappedBy = "program", fetch = FetchType.LAZY)
    @OrderBy("orderIndex asc")
    private List<Education> educationList = new ArrayList<>();

    @OneToMany(mappedBy = "program", fetch = FetchType.LAZY)
    private List<MyUser> myUserList = new ArrayList<>();

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "test_id")
    private PracticalTest practicalTest;

    public Program(int orderIndex, String themeName) {
        this.orderIndex = orderIndex;
        this.themeName = themeName;
    }

    public Program(long id) {
        this.id = id;
    }

    //other code here....

}

教育实体:

@Entity
@ToString(exclude = {"program", "myUserList"})
@Getter
@Setter
@NoArgsConstructor
public class Education implements Comparable<Education>{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String link;
    @Column(name = "order_index")
    private int orderIndex;
    private String type;
    private String task;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "program_id")
    private Program program;

    @OneToMany(mappedBy = "education", fetch = FetchType.LAZY)
    private List<MyUser> myUserList = new ArrayList<>();

    public Education(String link, int orderIndex, String task, Program program) {
        this.link = link;
        this.orderIndex = orderIndex;
        this.task = task;
        this.program = program;

    }

    //other code here....
}

计划回购:

@Repository
public interface ProgramRepo extends CrudRepository<Program, Long> {
    Optional<Program> findByPracticalTest(PracticalTest practicalTest);
    Optional<Program> findByOrderIndex(int orderIndex);
    List<Program> findByIdBetween(long start, long end);

}

教育回购:

@Repository
public interface EducationRepo extends CrudRepository<Education, Long> {
    Optional<Education> findByProgramAndOrderIndex(Program program, int orderIndex);

    @Query("select MAX(e.orderIndex) from Education e where e.program.id = ?1")
    int findLastEducationIndexByProgramId(long programId);
}
yeotifhr

yeotifhr1#

我认为问题是在一个事务中创建的程序对象保存在另一个事务中。这就是为什么如果我把事务控制器,它的工作。解决问题有两种方法:
在控制器上没有事务:那么我必须首先保存教育对象,因为它有program id字段,然后保存program对象。
对于控制器上的事务:那么保存顺序没有关系,因为保存对象发生在一个事务中

相关问题