hibernate之多对多注解

hibernate问题之多对多注解

1 项目中遇到的问题

场景:学生(StudentBean)与科目(SubjectBean)多对多关系

数据库表设计:

//学生表
DROP TABLE IF EXISTS `t_stu`;
CREATE TABLE `t_stu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`stu_name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
`fk_class_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

//学生与科目关系表
DROP TABLE IF EXISTS `t_stu_subj`;
CREATE TABLE `t_stu_subj` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `fk_stu_id` bigint(20) DEFAULT NULL,
  `fk_subj_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;

//科目表
DROP TABLE IF EXISTS `t_subj`;
CREATE TABLE `t_subj` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `subj_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

在注解中配置了:

cascade=cascadeType.ALL//级联关系

测试:

//测试根据id,查询学生没有问题。
//但是在测试删除学生的方法等时候,会抛出异常:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 
Table 'hibernate.t_subj_t_stu' doesn't exist

//结果hibernate告诉我'hibernate.t_subj_t_stu'中间表不存在,what?
//我的中间表名本来是:t_stu_subj,结果hibernate给我拼接成了t_subj_t_stu???

异常信息截图:
这里写图片描述

2 问题分析与解决

2.1 分析运行流程

测试代码:

//  @Ignore
    @Test
    public void testSelectStudentById() {
        //查询学生
        StudentBean stu = studentServiceImpl.selectStudentById(8L);
        System.out.println(stu);
        //删除学生
        studentServiceImpl.deleteStudent(stu);
    }

程序流程:

  • 1.根据学生id,查询学生。执行ok
    这里写图片描述
  • 2.查询出学生后,执行删除学生动作,会先查询学生里面的科目(因为在注解中配置了级联关系为ALL)。执行ok
    这里写图片描述
  • 3.科目查询出来后,就要删除科目,那么删除科目,就会再查询科目里面是否还有学生。
    这里写图片描述

那么问题出来了:hibernate框架在根据科目查询科目里面的学生的时候,把中间表表名拼错了

2.2 定位问题

问题极有可能是在学生实体(SudentBean)、科目实体(SubjectBean),的注解里配置manytomany的地方。

学生实体:

@Entity//实体
@Table(name="t_stu")//对应数据库中的表
public class StudentBean {
    @Id
    @GenericGenerator(name="hibernate.identity",strategy="identity")//表示hibernate的主键增长策略
    @GeneratedValue(generator="hibernate.identity")//引用上面的name
    private Long id;

    @Column(name="stu_name",length=20)
    private String stuName;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="fk_class_id")
    private ClassBean cls;

    @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    //cascade:级联关系
    //fetch:抓取机制,懒加载
    @JoinTable(name="t_stu_subj",joinColumns=@JoinColumn(name="fk_stu_id"),inverseJoinColumns=@JoinColumn(name="fk_subj_id"))
    //name:中间表表名
    //joinColumns:本类(StudentBean)在中间表上的外键
    //inverseJoinColumns:另外一个类(SubjectBean)在中间表上的外键
    private Set<SubjectBean> subjs;

    public Set<SubjectBean> getSubjs() {
        return subjs;
    }
    public void setSubjs(Set<SubjectBean> subjs) {
        this.subjs = subjs;
    }
    public StudentBean() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    public ClassBean getCls() {
        return cls;
    }
    public void setCls(ClassBean cls) {
        this.cls = cls;
    }
    @Override
    public String toString() {
        return "StudentBean [id=" + id + ", stuName=" + stuName + "]";
    }
}

科目实体:

@Entity//实体
@Table(name="t_subj")//对应数据库中的表
public class SubjectBean implements Serializable {

    @Id
    @GenericGenerator(name="hibernate.identity",strategy="identity")//表示hibernate的主键增长策略
    @GeneratedValue(generator="hibernate.identity")//引用上面的name
    private Long id;

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

    @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    //在这里科目实体中没有指定中间表。
    private Set<StudentBean> stus;

    public Long getId() {
        return id;
    }

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

    public String getSubjName() {
        return subjName;
    }

    public void setSubjName(String subjName) {
        this.subjName = subjName;
    }

    public Set<StudentBean> getStus() {
        return stus;
    }

    public void setStus(Set<StudentBean> stus) {
        this.stus = stus;
    }

    @Override
    public String toString() {
        return "SubjectBean [id=" + id + ", subjName=" + subjName +  "]";
    }
}

可以看到学生实体在注解中指定了中间表,科目实体在注解中并没有指定中间表。

因此hibernate在根据科目查询学生的时候,不知道中间表,所以发生中间表表名拼接错误

2.3 解决方案

解决方案自然是在科目实体中指定中间表

3 结论

那么得出结论:以后在多对多的注解中,有必要在两个实体Bean中都指定出中间表

另外一点体会:在我们配置了级联关系为ALL的时候,两个实体的耦合度极高,例如:我在删除学生的时候,还要先查学生里面的科目有没有,有就要删科目,那么删除科目,就要先查询科目里面的学生有没有,如果有,那么就又要删学生…如此循环,真的是牵一发而动全身,而且这种循环的删除如果数据量过大,好像就没有尽头一样……那么最后就会变成,我本来只是想删除一条学生记录,结果,学生表所有记录以及科目表所有记录,全都没了。

基于上面的体会:建议我自己,以后cascade属性,尽量不去配置,所有的级联关系在业务中由自己去控制。以便于好灵活处理。


以上都是我初略总结的。如果您感觉有不同的观点,那么肯定你是对的。仅供参考,还望多多指点。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值