[英]Spring MVC Transactional in dao service and controller layers
I'm using Spring MVC with Spring data.我正在使用 Spring MVC 和 Spring 数据。
Simple example of my problem:我的问题的简单示例:
My dao Service class:我的道服务class:
@Service
@AllArgsConstructor
@Transactional
public class FooService{
private FooRepository fooRepo;
public Foo save(Foo foo){
return fooRepo.save(foo);
}
}
and controller:和 controller:
@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";
}
}
If I remove @Transaction annotation from controller class, method save will not update foo object.如果我从 controller class 中删除 @Transaction 注释,则保存方法不会更新 foo object。 And I don't understand why I should mark controller by @Transactional annotation if I already mark service class by this annotation?
如果我已经通过此注释标记了服务 class,我不明白为什么我应该通过 @Transactional 注释标记 controller ?
############ UPDATE #################### ############ 更新 ####################
Simple detailed description:简单详细说明:
I have Program and Education entities.我有计划和教育实体。 One Program has many Education, Education entity has foreign key program_id.
一个 Program 有多个 Education,Education 实体有外键 program_id。 There is a page with Program form, there are fields: program id, program theme,..., and field with a list of education id separated by commas.
有一个带有 Program form 的页面,有字段:program id,program theme,...,以及带有以逗号分隔的教育 id 列表的字段。
I'm trying to update the education list at the program, so I add a new education id at the page form and click save.我正在尝试更新程序中的教育列表,因此我在页面表单中添加了一个新的教育 ID,然后单击保存。 Through debugger I see, that new education has appeared in the program, but changes do not appear in the database.
通过调试器我看到,程序中出现了新的教育,但数据库中没有出现变化。
@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;
}
}
ProgramService:节目服务:
@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);
}
}
}
Education service教育服务
@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);
}
}
Program entity:程序实体:
@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....
}
Education entity:教育单位:
@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....
}
Program repo:程序回购:
@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);
}
Education repo:教育回购:
@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);
}
I think the problem is program object created in one transaction and saved in another.我认为问题是程序 object 在一个事务中创建并保存在另一个事务中。 That's why if I put Transactional on controller it works.
这就是为什么如果我将 Transactional 放在 controller 上它可以工作。 There are two ways to solve the problem:
有两种方法可以解决问题:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.