Hibernatre(JPA存储库)-尝试获取多对多关系数据

时间:2020-03-26 14:04:56

标签: java hibernate spring-boot jpa spring-data-jpa

我目前正在学习Hibernate,但我一直坚持从hibernate(spring-jpa)中的多对多关系中获取数据。我正在尝试通过ID获取数据,但它根本无法正常工作。

我知道这是不现实的,但是可以从很多人那里取一本书。

问题是,在哪个类中放入EAGER访存类型无关紧要,在第二类中将LAZY访存类型放进去,这是无关紧要的:

LazyInitializationException:无法延迟初始化角色集合

但是,如果我在两个类中都使用EAGER提取类型,则会抛出StackOverFlowError

多对多逻辑是这样的,我们在图书馆有一本书,我们有很多人可以拿很多书,而很多人可以拿本书。

我试图将EAGER的获取都放在这两个类中,但这给了我StackOverFlowError

我不得不提到我正在使用JpaRepository界面。

Person类:

@Entity
@Table(name = "persons")
public class Person { // (in library)

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    @Column(name = "name")
    private String name;

    @Column(name = "number_card")
    private int numberCard;

    @Column(name = "time_of_account_creating")
    @Temporal(TemporalType.DATE)
    private Date date;

    @ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
    List<Book> books = new ArrayList<>();

    public Person() {
        this.date = new Date();
    }

    public Person(String name, int numberCard) {
        this.name = name;
        this.numberCard = numberCard;
        this.date = new Date();
    }

    public void addBook(Book book) {
        this.books.add(book);
    }
    // gettters and setters

Book类:

@Entity
@Table(name = "books")
public class Book { // (in library)

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    @Column(name = "release_year")
    private int releaseYear;

    @Column(name = "name")
    private String name;

    @ManyToMany(mappedBy = "books", cascade=CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Person> persons = new ArrayList<>();

    public Book() {
    }

    public Book(int releaseYear, String name) {
        this.releaseYear = releaseYear;
        this.name = name;
    }

    public List<Person> getPersons() {
        return persons;
    }

    public void addPerson(Person person) {
        this.persons.add(person);
    }
    // getters and setters

@SpringBootApplication中的主要方法:

public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(HibernateDemoApplication.class, args);
        PersonRepository personRepository = context.getBean(PersonRepository.class);

        System.out.println(personRepository.findById(4));
}

application.property:

spring.datasource.url = jdbc:mysql://localhost:3306/${DB}
spring.datasource.username = ${username}
spring.datasource.password = ${password}
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql=true

输出控制台:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

2020-03-26 15:59:27.228  INFO 18912 --- [           main] c.e.h.HibernateDemoApplication           : Starting HibernateDemoApplication on abu with PID 18912 (/home/yoav/hibernateDemo/target/classes started by yoav in /home/yoav/hibernateDemo)
2020-03-26 15:59:27.232  INFO 18912 --- [           main] c.e.h.HibernateDemoApplication           : No active profile set, falling back to default profiles: default
2020-03-26 15:59:28.450  INFO 18912 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-03-26 15:59:28.625  INFO 18912 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 131ms. Found 6 JPA repository interfaces.
2020-03-26 15:59:31.886  INFO 18912 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-03-26 15:59:31.928  INFO 18912 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-26 15:59:31.929  INFO 18912 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-26 15:59:32.101  INFO 18912 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-26 15:59:32.101  INFO 18912 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 4687 ms
2020-03-26 15:59:32.636  INFO 18912 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-03-26 15:59:33.001  INFO 18912 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.12.Final
2020-03-26 15:59:33.631  INFO 18912 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-03-26 15:59:35.055  INFO 18912 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-26 15:59:37.374  INFO 18912 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-26 15:59:37.689  INFO 18912 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect
Hibernate: alter table students add constraint FKrpifpqwvgu2pg2lib5c787vs foreign key (laptop_id) references laptops (id)
2020-03-26 15:59:41.092  INFO 18912 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-03-26 15:59:41.105  INFO 18912 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-03-26 15:59:42.982  WARN 18912 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-03-26 15:59:43.406  INFO 18912 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-26 15:59:44.775  INFO 18912 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-26 15:59:45.009  INFO 18912 --- [           main] c.e.h.HibernateDemoApplication           : Started HibernateDemoApplication in 18.339 seconds (JVM running for 18.756)
Hibernate: select person0_.id as id1_5_0_, person0_.time_of_account_creating as time_of_2_5_0_, person0_.name as name3_5_0_, person0_.number_card as number_c4_5_0_, books1_.persons_id as persons_1_6_1_, book2_.id as books_id2_6_1_, book2_.id as id1_0_2_, book2_.name as name2_0_2_, book2_.release_year as release_3_0_2_ from persons person0_ left outer join persons_books books1_ on person0_.id=books1_.persons_id left outer join books book2_ on books1_.books_id=book2_.id where person0_.id=?
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.hibernateDemo.models.Book.persons, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
    at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:621)
    at java.lang.String.valueOf(String.java:2994)

我不得不再说一遍,如果我在两个类中都添加了EAGER提取类型,则会得到StackOverFlowError

1 个答案:

答案 0 :(得分:0)

我知道这个线程已经使用了几个月,但是将来会问:

问题:

您隐藏Person#toString方法。在方法中,您可以访问书籍字段。很好,因为fetchtype为EAGER。问题是您访问Book#toString,并在其中访问人员字段。此字段为LAZY。由于您将不会在事务内访问此字段,因此始终会获得LazyInitializationException。

解决方案:

  1. 打开交易。 (google jpa交易)
  2. 加载实体。
  3. 访问惰性字段。会起作用的!
  4. 关闭事务:注意,所有应用于事务中已加载实体的更改都将自动保存到数据库中,无需通过存储库DAO手动保存它们!