【hibernate】之关于关联映射的综合应用
1、关联映射如何处理业务逻辑
2、如何指定中间表
3、如何进行级联操作
4、如何解决Hibenrate建表过程中主键错乱问题
现在有三张表
Student(学生表),Course(课程表),Score(学生,课程,分数,表)
那么我们分析业务逻辑可知,学生和课程是多对多的关系,学生和分数表是一对多的关系,课程和分数也是一对多的关系。
直接看Annotations配置,在这里我所有的配置都是双向关联,这样在分数,课程,学生,之中,可以任意找到彼此!
@Entity @Table(name="c_course") public class Course { private Integer id; private String coursename; private Set<Student> students=new HashSet<Student>(); private Set<Score> scores=new HashSet<Score>(); @OneToMany(mappedBy="course")//必须指定关系由多的一方维护 public Set<Score> getScores() { return scores; } public void setScores(Set<Score> scores) { this.scores = scores; } @ManyToMany//指定中间表是s_score @JoinTable(name="s_score",joinColumns={@JoinColumn(name="course_id")}, inverseJoinColumns={@JoinColumn(name="student_id")}) public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="c_coursename") public String getCoursename() { return coursename; } public void setCoursename(String coursename) { this.coursename = coursename; } }
(课程表Course)
@Entity @Table(name="t_student") public class Student { private Integer id; private String name; private Set<Course> courses=new HashSet<Course>(); private Set<Score> scores=new HashSet<Score>(); @OneToMany(mappedBy="student") public Set<Score> getScores() { return scores; } public void setScores(Set<Score> scores) { this.scores = scores; } @ManyToMany @JoinTable(name="s_score",joinColumns={@JoinColumn(name="student_id")}, inverseJoinColumns={@JoinColumn(name="course_id")}) public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="s_name") public String getName() { return name; } public void setName(String name) { this.name = name; } }
(Student学生表)
@Entity @Table(name="s_score") public class Score { private Integer id; private String score; private Student student; private Course course; @ManyToOne(cascade={CascadeType.ALL})//配置级联操作 @JoinColumn(name="student_id") public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } @ManyToOne(cascade={CascadeType.ALL}) @JoinColumn(name="course_id") public Course getCourse() { return course; } public void setCourse(Course course) { this.course = course; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="s_score") public String getScore() { return score; } public void setScore(String score) { this.score = score; } }
(分数表,也是中间关联表,Score)
那么问题来了,采用Hibernate自动建表,我们看一下建表语句
12:11:08,169 DEBUG SchemaExport:415 - create table c_course ( id integer not null auto_increment, c_coursename varchar(255), primary key (id) ) 12:11:08,333 DEBUG SchemaExport:415 - create table s_score ( id integer not null, s_score varchar(255), course_id integer, student_id integer not null auto_increment, primary key (student_id, course_id) ) 12:11:08,455 DEBUG SchemaExport:415 - create table t_student ( id integer not null auto_increment, s_name varchar(255), primary key (id) )
我们发现在建表是Hibernate默认将student_id和course_id联合作为主键使用,且student_id还是自增
这样显然是不对的,在这里我也实在想不到,好的办法,智能采用一个笨的办法,手动改回来!
大家最好是采用工具操作,因为,你直接create的话,student_id和course_id关联不上student,course的主键ID
ok,到这里,我们的建表基本完成,下面JunitTest
@Test public void add(){ try { Configuration cfg=new Configuration(); cfg.configure(); SessionFactory sessionFactory=cfg.buildSessionFactory(); Session session=sessionFactory.openSession(); session.beginTransaction(); Student student1=new Student(); student1.setName("张三1"); // Student student2=new Student(); // student2.setName("李四"); Course c1=new Course(); c1.setCoursename("数学1"); Course c2=new Course(); c2.setCoursename("语文1"); // session.save(student1); // session.save(c1); // session.save(student2); // session.save(c2); Score score=new Score(); score.setScore("901"); score.setCourse(c1); score.setStudent(student1); //张三数学90分 session.save(score); Score score2=new Score(); score2.setCourse(c2); score2.setScore("100"); score2.setStudent(student1); session.save(score2); session.getTransaction().commit(); session.close(); sessionFactory.close(); } catch (HibernateException e) { e.printStackTrace(); } }
ok,大家可以看出,为什么我们最后只需要保存score就行呢?没有session.save()student和course,为什么也能直接保存数据库中?这就是
cascade={CascadeType.ALL}
级联操作,当然我这里配置的是ALL,其实还有4个参数,大家可以自己慢慢研究!
以上添加成功,那么我们直接load试一下
public void findTest(){ Configuration cfg=new Configuration(); cfg.configure(); SessionFactory sessionFactory=cfg.buildSessionFactory(); Session s=sessionFactory.openSession(); s.beginTransaction(); Student student=(Student)s.load(Student.class, 2); System.out.println("学生姓名"+student.getName()); for(Course course:student.getCourses()){ System.out.print("--------"+"课程名称"+course.getCoursename()); for(Score score:course.getScores()){ System.out.println("--------"+"课程分数"+score.getScore()); } } s.getTransaction().commit(); s.close(); sessionFactory.close(); }
也没问题
update呢?
public void update(){ Configuration cfg=new Configuration(); cfg.configure(); SessionFactory sessionFactory=cfg.buildSessionFactory(); Session s=sessionFactory.openSession(); s.beginTransaction(); Student student=(Student)s.load(Student.class,1); //我们现在把查询出来语文成绩改为0分 for(Course course:student.getCourses()){ System.out.println(course.getCoursename()); if("语文".equals(course.getCoursename())){ for(Score score:course.getScores()){ if(score.getStudent().getId().equals(student.getId())){ score.setScore("0"); s.update(score); } } } } s.getTransaction().commit(); s.close(); sessionFactory.close(); }
删除?
@Test public void deleteObj(){ Configuration cfg=new Configuration(); cfg.configure(); SessionFactory sessionFactory=cfg.buildSessionFactory(); Session s=sessionFactory.openSession(); s.beginTransaction(); Student student=(Student)s.load(Student.class,3); s.delete(student); s.getTransaction().commit(); s.close(); sessionFactory.close(); }
ok,到这里基本上,完成Hibernate关联映射的基本操作!
注意,多对多删除的时候,我们一般删除的是中间表数据?但是往往由于外键关联的关系,Hibernate会删除另一张表中的数据来解除关联关系,这就不对了!后续再探讨!