repository保存的工作方式与SpringBoot中的controller或commandlinerunner不同

sbtkgmzw  于 2021-07-09  发布在  Java
关注(0)|答案(4)|浏览(422)

我有两个例子,当从不同的上下文调用时,子实体save的工作方式不同。当我在没有级联父级的情况下更新子级时,修改后的数据也会被更新,但这只发生在从控制器调用时 CommandLineRunner 父级更新不会发生。
这是我的服务

@Service
public class BookService {

    private final BookAuthorRepository bookAuthorRepository;

    private final BookRepository bookRepository;

    @Autowired
    public BookService(BookAuthorRepository bookAuthorRepository, BookRepository bookRepository) {
        this.bookAuthorRepository = bookAuthorRepository;
        this.bookRepository = bookRepository;

    }

    public void updateBookAuth() {
        Book book = bookRepository.findById(3).get();
        book.setName("should not update");
        book.getBookAuthor().setName("new name");
        bookAuthorRepository.save(book.getBookAuthor());
    }
}

当我从下面的类调用它时,只有 BookAuthor 名称被更新,这是正确的行为。

@Component
public class MyRunner implements CommandLineRunner {

    @Autowired
    private BookService bookService;

    @Override
    public void run(String... args) throws Exception {
         bookService.updateBookAuth();
    }
}

但如果我从控制器调用此服务方法:

@RestController
public class BookController {

    private final BookService bookService;

    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping("/")
    public void test() {
        bookService.updateBookAuth();
    }
}

然后两者 Book 姓名和 BookAuthor 名字更新了,但我不明白为什么 Book 名称已更新。
以下是实体:

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @OneToOne
    private BookAuthor bookAuthor;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public BookAuthor getBookAuthor() {
        return bookAuthor;
    }

    public void setBookAuthor(BookAuthor bookAuthor) {
        this.bookAuthor = bookAuthor;
    }
}

@Entity
public class BookAuthor {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @OneToOne(mappedBy = "bookAuthor")
    private Book book;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }
}

我还考虑了事务性,如果我在方法上写事务,那么所有上下文都会打开,父-子上下文都会更新,但在这种情况下,我不使用它,为什么会发生这种情况?

34gzjxbg

34gzjxbg1#

是否管理:false
当您从bookcontroller调用updatebookauth函数时,它将打印:

2wnc66cl

2wnc66cl2#

当您从存储库中获取图书实体时,可以检查它是否仍处于管理状态。为此,我们可以在服务中直接插入实体管理器,并添加如下日志语句:

@Service
    @RequiredArgsConstructor
    public class BookService {

      private final BookBookAuthorRepository bookAuthorRepository;
      private final BookRepository bookRepository;
      private final EntityManager entityManager;

      public void updateBookAuth() {
        Book book = bookRepository.findById(1L).get();

        System.out.println("----------> is managed: " + entityManager.contains(book));

        book.setName("should not update");
        book.getBookAuthor().setName("new name");
        bookAuthorRepository.save(book.getBookAuthor());
      }

    }

现在,当您从myrunner调用updatebookauth函数时,它将打印:

hgb9j2n6

hgb9j2n63#

要真正理解发生了什么,您需要了解hibernate是如何工作的,尤其是hibernate会话。这是一篇好文章https://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate . 在bookservice类中,您专门为bookauthor设置了一个新名称,因为这个事务是会话的一部分,所以它也会更新它。springdatajpa非常有用,但是您必须熟悉hibernate才能完全理解其行为。

aiqt4smr

aiqt4smr4#

管理:正确
这意味着在rest控制器的情况下,数据库操作在单个持久会话中执行,因此book和author实体都被更新。
在命令行运行程序的情况下,图书实体查询在单独的持久性会话中执行,然后分离,作者实体更新在单独的持久性会话中执行,因为图书实体更新丢失。
这是因为springweb框架默认情况下将持久性会话和请求生命周期联系在一起。这样实现是为了防止视图层中的延迟初始化关联出现问题。
要防止此行为,可以使用application.properties文件中的以下设置禁用它:
spring.jpa.open in view=假
请阅读本文中有关此功能的更多信息

相关问题