我将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);
}
1条答案
按热度按时间yeotifhr1#
我认为问题是在一个事务中创建的程序对象保存在另一个事务中。这就是为什么如果我把事务控制器,它的工作。解决问题有两种方法:
在控制器上没有事务:那么我必须首先保存教育对象,因为它有program id字段,然后保存program对象。
对于控制器上的事务:那么保存顺序没有关系,因为保存对象发生在一个事务中