V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
NoKey
V2EX  ›  程序员

jpa 多表关联,头痛,求大佬指点

  •  
  •   NoKey · 2021-02-09 12:05:35 +08:00 · 3616 次点击
    这是一个创建于 1420 天前的主题,其中的信息可能已经有所发展或是发生改变。

    之前用的 mybatis,各种查询都是自己写,非常快捷

    最近开始练习使用 jpa

    涉及到多表关联,用起来有点头痛

    我写了一个 demo,涉及 3 张表,学生,老师,学生-老师关联表

    Student

    @Entity
    @Table
    @Data
    public class Student {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(length = 32)
        private String name;
    
        @JoinTable(name = "student_and_teacher",
                joinColumns = {@JoinColumn(name = "studentId", referencedColumnName = "id")},
                inverseJoinColumns = {@JoinColumn(name = "teacherId", referencedColumnName = "id")},
                foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT),
                inverseForeignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
        @ManyToMany
        private List<Teacher> teachers;
    
    
    
    }
    
    

    其中的 @JoinTable 就是表明要关联到学生-老师关联表中去

    Teacher 类

    @Entity
    @Table
    @Data
    public class Teacher {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(length = 32)
        private String name;
    
    
    }
    

    然后就是学生-老师关联

    student-teacher

    @Entity
    @Table(name = "teacher_and_lesson")
    @Data
    public class StudentAndTeacher {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column
        private Long studentId;
    
        @Column
        private Long teacherId;
    }
    
    

    这一套工作的很好,可以查询出学生-老师关系,添加一些数据后,执行查询学生的方法,可以得到

    {"id":1,"name":"Joe","teachers":[{"id":1,"name":"王老师"},{"id":2,"name":"李老师"}]}
    
    

    但是,这个时候,我想增加一个 课程表,设定每个老师教一个课程

    Lesson

    @Entity
    @Table
    @Data
    public class Lesson {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(length = 32)
        private String name;
    }
    
    

    然后,就是增加老师-课程关联关系

    TeacherAndLesson

    @Entity
    @Table
    @Data
    public class TeacherAndLesson {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column
        private Long lessonId;
    
        @Column
        private Long teacherId;
    }
    
    

    这样的话,就需要在老师实体类中,增加一个课程的关联关系

    
    @Entity
    @Table
    @Data
    public class Teacher {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(length = 32)
        private String name;
    
        @JoinTable(name = "teacher_and_lesson",
                joinColumns = {@JoinColumn(name = "teacherId", referencedColumnName = "id")},
                inverseJoinColumns = {@JoinColumn(name = "lessonId", referencedColumnName = "id")},
                foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT),
                inverseForeignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
        @OneToOne
        private Lesson lesson;
    
    }
    
    

    这样写了之后,程序启动完成,自动创建的 teacher_and_lesson 表中,会多出一个 student_id 出来,

    没搞懂是为啥。😭

    求大佬指点,谢谢。。。

    30 条回复    2021-02-10 15:28:24 +08:00
    overthemoon
        1
    overthemoon  
       2021-02-09 16:03:40 +08:00
    多表打死不用 jpa,mybatis 不香吗
    hly9469
        2
    hly9469  
       2021-02-09 16:15:19 +08:00 via iPhone
    我最终体会就是别用 jpa 的表关联了,关联资源代码里手动组装
    yaojiarui
        3
    yaojiarui  
       2021-02-09 16:20:47 +08:00
    不用加上那些约束注解,直接在 jpaRepository 里面写原生 sql 就好
    @Query(value = "select * from a inner join b on a.id = b.id inner join c on a.id = c.id", nativeQuery = true)
    Obj someMethod()
    gengzi
        4
    gengzi  
       2021-02-09 16:23:22 +08:00
    写原生 sql 即可
    seashell84
        5
    seashell84  
       2021-02-09 16:23:54 +08:00 via Android
    有关联别用 jpa,换 jdbctemplate
    themostlazyman
        6
    themostlazyman  
       2021-02-09 16:44:33 +08:00
    @NoKey
    “@Entity
    @Table(name = "teacher_and_lesson")
    @Data
    public class StudentAndTeacher {”
    你这个表名指定的对吗?
    AkideLiu
        7
    AkideLiu  
       2021-02-09 16:44:37 +08:00
    jpa 一时爽,多表火葬场
    Cryse
        9
    Cryse  
       2021-02-09 17:17:19 +08:00   ❤️ 1
    只有 #6 认真看代码了,其他人代码都没看就喷 JPA 多表是什么鬼...

    你的 Teacher 表和 Lesson 表关联用的是 teacher_and_lesson, 而 StudentAndTeacher 的 @Table 你也指定的是 teacher_and_lesson, 这个 StudentAndTeacher 里面还有个 @Column 是 studentId, 这不就是原因吗?
    22k
        10
    22k  
       2021-02-09 19:07:06 +08:00
    试试用 querydsl-jpa,java 式编程
    NoKey
        11
    NoKey  
    OP
       2021-02-09 22:10:13 +08:00
    @themostlazyman 谢谢,修改了,然后跑不起来了,报错
    is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Export identifier [student_and_teacher] encountered more than once
    NoKey
        12
    NoKey  
    OP
       2021-02-09 22:10:44 +08:00
    @Cryse 谢谢,修改过来了,但是现在跑不起来了😓
    crclz
        13
    crclz  
       2021-02-09 23:00:07 +08:00
    1. 业务类的,手动查
    2. 查询类的,用 EntityManager 写 SQL ( JPQL )查询,反正 IDEA 的提示是挺好用的。
    NoKey
        14
    NoKey  
    OP
       2021-02-09 23:02:01 +08:00
    我重新调整了一下代码
    student teacher student-teacher 这三个一起,ok 了
    teacher lesson teacher-lesson 这三个一起,ok 了
    然而,这几个混到一起的时候,就运行不起来了,报错

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Export identifier [student_and_teacher] encountered more than once
    bestu
        15
    bestu  
       2021-02-09 23:34:48 +08:00 via Android
    one to one 不应该 join column 吗
    evi1j
        16
    evi1j  
       2021-02-10 08:27:22 +08:00 via Android
    EntityManager 不是美滋滋
    Thetruechar
        17
    Thetruechar  
       2021-02-10 09:15:18 +08:00 via Android
    别用 orm 直接写 sql
    chanchan
        18
    chanchan  
       2021-02-10 09:30:17 +08:00 via Android
    中间表不需要自己管理,我平时用 join column 感觉更方便。
    NoKey
        19
    NoKey  
    OP
       2021-02-10 10:12:39 +08:00
    @chanchan join column 就是往某个表加个字段,是么,这个不是中间表吧
    NoKey
        20
    NoKey  
    OP
       2021-02-10 10:13:07 +08:00
    @bestu join column 造不出中间表吧
    Kontinue
        21
    Kontinue  
       2021-02-10 10:52:25 +08:00
    我觉得 Spring Data JPA 就是要简化这些东西。同意楼上的,要不就自己手动拼,java8 的 strean 也挺好用的,要不就 querydsl 。通过注解强关联也行,但前提是你对 Hribernate 很熟悉,例如对象管理、dirty check 、级联操作等,否则容易出现问题
    beginor
        22
    beginor  
       2021-02-10 10:54:31 +08:00 via Android
    NHibernate 用户路过, 原理都是一样的。

    多对多不需要自己管理中间表的,也就是不需要在映射中体现这个表的映射, 只要在多对多映射中说明这个表就行。

    可以先试一下 xml 映射, 这个才是 hibernate 的精髓, 不管你怎么写 annotation, 最终都会被翻译成 xml 映射, 所以 xml 映射才是最直接的, 先掌握 xml 映射, 再去学习这些 annotation 映射, 如果直接上手就是 annotation 的话, 碰到稍微高级的映射就不知所措了。

    hibernate 还是值得认真学习一下的, 不管是 .net 还是 java,hibernate 都是非常优秀的 orm 框架, 用好了的话在后台管理中还是非常省心和靠谱的。
    beginor
        23
    beginor  
       2021-02-10 10:57:28 +08:00 via Android
    反观那些张嘴就说 jpa 和 hibernate 火葬场的, 多数都是不怎么懂 jpa 和 hibernate 的。
    NoKey
        24
    NoKey  
    OP
       2021-02-10 11:48:59 +08:00
    @Kontinue 通过注解关联起来的好处是后面进行查询,插入等操作的时候,很方便,不用单独去写查询语句,写 sql 或者类 sql 去做这些事当然没问题,现在我就是想试试看,代码中不加入 sql,能否完成平时能做的事情
    chanchan
        25
    chanchan  
       2021-02-10 13:17:52 +08:00 via Android
    @NoKey join column 确实造不出中间表。中间表的话写好 @manytomany 等注解,hibernate 会自己建
    XiLemon
        26
    XiLemon  
       2021-02-10 13:25:18 +08:00 via iPhone
    看到 JPA 和头痛,我慌了
    bestu
        27
    bestu  
       2021-02-10 13:45:31 +08:00 via Android
    @NoKey 造不出来,因为一对一直接用外键映射即可,一对多和多对一也是,只有在多对多才会维护中间表,不过也是 jpa 自己去维护,使用的话直接调用就可以
    NoKey
        28
    NoKey  
    OP
       2021-02-10 15:08:26 +08:00
    谢谢各位,问题解决了,报错是 postgresql 的问题,之前用的是 mysql,对 pg 不熟,问题已解决,我上面的代码问题就在于其中一个表名写错了
    tedzhou1221
        29
    tedzhou1221  
       2021-02-10 15:10:29 +08:00
    多表关联写的时候爽,后面维护、修改逻辑 那才叫头痛。如果 SQL 别人写得乱一点,这些 SQL 基本就废了。
    gjkv86
        30
    gjkv86  
       2021-02-10 15:28:24 +08:00 via iPhone
    这看着不是 jpa 的锅,是你把 jpa 用成了面向数据库编程。jpa 首先要对象建模,你这设计看不出有这方面的内容。你把对象关系捋清楚在去熟悉 hibernate 怎么用,前提是你得有这个能力。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1007 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 104ms · UTC 20:00 · PVG 04:00 · LAX 12:00 · JFK 15:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.