(原创)Hibernate 使用过程中(尤其是多对多关联中的级联保存和级联删除)的注意事项(基于项目的总结)
一.先上知识点:
1.hibernate多对多关联关系中最重要的参数是(基于配置文件xxx.hbm.xml文件形式):
1):inverse属性,如果设置inverse=“true”就代表让对方参与维护第三方表格。//这个属性特别重要,多对多关系中最好有且只有一个维护第三方表格,如果两方都维护第三方表格,那么可能第三方表格会重复维护现象(有可能会造成第三方表格联合主键的重复)
2):lazy属性,这个属性代表延迟加载与否,在讲这个属性之前,先讲一个东西,就是在hibernate多对多关联关系中,在双方的类中都会出现一个Set<XXX>这个集合属性(在读取过程中,这个set集合会被hibernate封装成persistentSet集合),当在xxx.hbm.xml文件中设置lazy=“false”时,就说明,不会让延迟加载起作用,那么这个延迟加载加载什么东西呢?延迟加载的东西就是刚刚提到的Set<XXX>集合中的东西,打个比方,现在有多对多的User类和Educate类(员工多对多培训课程),在User类中的java代码如下:
package com.ssh.entities; import java.io.Serializable; import java.util.Date; import java.util.HashSet; import java.util.Set; public class User { private Long id;//员工编号 private String name;//员工用户名 private String password;//登录密码 private Byte sex;//性别 private Date birthday;//生日 private Date createtime;//创建时间 private Byte isadmin;//是否为管理员 private String content;//人员简介 private Set<Educate> educate=new HashSet<Educate>(); public Set<Educate> getEducate() { return educate; } public void setEducate(Set<Educate> educate) { this.educate = educate; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Byte getSex() { return sex; } public void setSex(Byte sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public Byte getIsadmin() { return isadmin; } public void setIsadmin(Byte isadmin) { this.isadmin = isadmin; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public User(Long id, String name, String password, Byte sex, Date birthday, Date createtime, Byte isadmin, String content) { this.id = id; this.name = name; this.password = password; this.sex = sex; this.birthday = birthday; this.createtime = createtime; this.isadmin = isadmin; this.content = content; } public User() { } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", password=" + password + ", sex=" + sex + ", birthday=" + birthday + ", createtime=" + createtime + ", isadmin=" + isadmin + ", content=" + content + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } }
上述代码中标红的就是一个Set<Educate>集合。
那我在User.hbm.xml文件中配置lazy="false"的时候,就是说明,当我前端或者后台需要加载user对象时,那这个带有对方(Educate类)的集合也会通过第三方表格顺带着把Educate类中的信息一起加载出来,
反之,当lazy="true"的时候就默认lazy延迟加载工程开启,也就是说Set集合中的东西,现在不会给加载出来。
3)cascade属性,我在项目中一般配置 cascade=“all”,这个就代表在级联操作时(保存、删除)会不会级联到关联的一方
总结:基于配置文件的hibernate 多对多就是lazy和inverse属性在起关键作用。
2.hibernate 多对多中的级联保存和删除:
1)保存,保存一般情况下不仅保存自己一方的对象到数据库,还需要顺带着把和另一方的关系也保存到第三方表格,那么这时,如果是自己这方是负责维护第三方表格的,那么当编辑自己对象的信息时(将集合中塞入对方对象到集合中,代表与对方的关系),在执行hibernate的session的saveOrupdate()方法时,不仅保存自己对象到数据库中,同时也会把维护关系保存到第三方表格中,如果自己这方没有负责维护第三方表格,那么即便自己这方的Set集合中添加了对方对象的信息,当执行hibernate的session的 saveorUpdate()方法时,只能是把自己的信息保存到自己对应的数据库中,与对方的关系(通过set集合中的信息进行设置,通过第三方的数据表格体现)的第三方表格,没有发生更新,也就是说没有达到想要的结果。
现在举例说明一下:
User类(如上)和User.hbm.xml文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.ssh.entities.User" table="user"> <id column="id" name="id" type="java.lang.Long"> <generator class="native"></generator> </id> <property name="name" length="50" type="java.lang.String"/> <property name="password" length="50" type="java.lang.String"/> <property name="sex" length="4" type="java.lang.Byte"/> <property name="birthday" length="23" type="java.util.Date"/> <property name="createtime" length="23" type="java.util.Date"/> <property name="isadmin" length="4" type="java.lang.Byte"/> <property name="content" length="2000" type="java.lang.String"/> <set name="educate" table="user_educate" lazy="false" cascade="all" inverse="false"> <key column="user_id"></key> <many-to-many class="com.ssh.entities.Educate" column="educate_id"></many-to-many> </set> </class> </hibernate-mapping>
Educate类和Educate.hbm.xml
package com.ssh.entities; import java.io.Serializable; import java.util.Date; import java.util.HashSet; import java.util.Set; public class Educate { private Long id;//培训标号 private String name;//培训名称 private String purpose;//培训目的 private Date begintime;//培训开始时间 private Date endtime;//培训结束时间 private String datum;//培训材料 private String teacher;//培训讲师 private String student;//培训人员 private Date createtime;//创建时间 private Byte educate;//培训是否完成 private String effect;//培训效果 private String summarize;//培训总结 private Set<User> user=new HashSet<User>(); public Set<User> getUser() { return user; } public void setUser(Set<User> user) { this.user = user; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPurpose() { return purpose; } public void setPurpose(String purpose) { this.purpose = purpose; } public Date getBegintime() { return begintime; } public void setBegintime(Date begintime) { this.begintime = begintime; } public Date getEndtime() { return endtime; } public void setEndtime(Date endtime) { this.endtime = endtime; } public String getDatum() { return datum; } public void setDatum(String datum) { this.datum = datum; } public String getTeacher() { return teacher; } public void setTeacher(String teacher) { this.teacher = teacher; } public String getStudent() { return student; } public void setStudent(String student) { this.student = student; } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public Byte getEducate() { return educate; } public void setEducate(Byte educate) { this.educate = educate; } public String getEffect() { return effect; } public void setEffect(String effect) { this.effect = effect; } public String getSummarize() { return summarize; } public void setSummarize(String summarize) { this.summarize = summarize; } public Educate(Long id, String name, String purpose, Date begintime, Date endtime, String datum, String teacher, String student, Date createtime, Byte educate, String effect, String summarize) { this.id = id; this.name = name; this.purpose = purpose; this.begintime = begintime; this.endtime = endtime; this.datum = datum; this.teacher = teacher; this.student = student; this.createtime = createtime; this.educate = educate; this.effect = effect; this.summarize = summarize; } public Educate() { } @Override public String toString() { return "Educate [id=" + id + ", name=" + name + ", purpose=" + purpose + ", begintime=" + begintime + ", endtime=" + endtime + ", datum=" + datum + ", teacher=" + teacher + ", student=" + student + ", createtime=" + createtime + ", educate=" + educate + ", effect=" + effect + ", summarize=" + summarize + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Educate other = (Educate) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.ssh.entities.Educate" table="educate"> <id column="id" name="id" type="java.lang.Long"> <generator class="native"></generator> </id> <property name="name" length="100" type="java.lang.String"></property> <property name="purpose" length="500" type="java.lang.String"/> <property name="begintime" length="23" type="java.util.Date"/> <property name="endtime" length="23" type="java.util.Date"/> <property name="datum" length="2000" type="java.lang.String"/> <property name="teacher" length="50" type="java.lang.String"/> <property name="student" length="50" type="java.lang.String"/> <property name="createtime" length="23" type="java.util.Date"/> <property name="effect" length="500" type="java.lang.String"/> <property name="educate" length="1" type="java.lang.Byte"/> <property name="summarize" length="2000" type="java.lang.String"/> <set name="user" table="user_educate" lazy="true" cascade="all" inverse="true"> <key column="educate_id"></key> <many-to-many class="com.ssh.entities.User" column="user_id"></many-to-many> </set> </class> </hibernate-mapping>
后台控制的java代码(保存和另一方关系的代码):
public String insertcourse(){ String[] para=request.getParameterValues("edupara"); String userid=request.getParameter("userid"); if(para.length>0){ User us=userService.getUser(Long.parseLong(userid)); Set<Educate> edus=new HashSet<Educate>(); edus=us.getEducate(); for(String str: para ){ Educate educate=educateService.getForEdu(Long.parseLong(str)); /* Set users=new HashSet<User>(); users=educate.getUser(); System.out.println(users.size()); users.add(us); System.out.println(users.size()); educate.setUser(users); educateService.saveorUpdate(educate); */ edus.add(educate); } us.setEducate(edus); userService.saveOrUpdate(us); } return "insertcourse";
2)删除级联的注意事项:
(1)如果不是User维持关联关系:
——若连接表user_educate中有参照user表中该记录的记录(即在user_educate表中存在user_id的记录)时,则删除失败。
——若连接表user_educate中没有参照user表中该记录的记录时,则可以成功地将该记录删除。
(2)如果是User维持关联关系:
——先将连接表user_educate中参照user表中该记录的记录删除,然后将该学生记录从user表中删除
最后附上后台处理的完整代码:
UserAction.java
package com.ssh.action; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.interceptor.ServletRequestAware; import com.opensymphony.xwork2.ActionSupport; import com.ssh.entities.Educate; import com.ssh.entities.User; import com.ssh.service.EducateService; import com.ssh.service.UserService; public class HumanAction extends ActionSupport implements ServletRequestAware{ /** * @author zhangshitong */ private static final long serialVersionUID = 1L; private String username; private String password; private UserService userService; private InputStream inputStream; private HttpServletRequest request; private Long id; private List<Educate> eduList; private List<User> userList; private EducateService educateService; private User userForEdit; public void setEducateService(EducateService educateService) { this.educateService = educateService; } public void setId(Long id) { this.id = id; } private User user; public void setUser(User user) { this.user = user; } public User getUser() { return user; } public InputStream getInputStream() { return inputStream; } public void setUserService(UserService userService) { this.userService = userService; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String adduser(){ user.setCreatetime(new Date()); //user.setId(null); System.out.println(user); userService.saveOrUpdate(user); user = null; return "add"; } public String list(){ System.out.println(userService.getForList()); request.setAttribute("user", userService.getForList()); return "list"; } public String select(){ User usertemp=userService.getUser(id); this.userForEdit=usertemp; request.setAttribute("signalUser", usertemp); return "edit"; } public String delete(){ userService.deleteUser(id); try { inputStream = new ByteArrayInputStream("1".getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block try { inputStream = new ByteArrayInputStream("0".getBytes("UTF-8")); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } return "ajax-success"; } public String modify(){ user.setEducate(userForEdit.getEducate()); user.setCreatetime(new Date()); System.out.println(user); userService.saveOrUpdate(user); return "modified"; } public String login(){ User user=userService.validName(username); if(user==null){ try { inputStream = new ByteArrayInputStream("3".getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ if(user.getPassword().equals(password)){ try {request.getSession().setAttribute("user", user); inputStream = new ByteArrayInputStream("1".getBytes("UTF-8")); //ctx.getSession().put("user", user); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }else{ try { inputStream = new ByteArrayInputStream("0".getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return "ajax-success"; } public String userselecteducate(){ System.out.println("这是查看员工选择课程时员工的id值:"+id); Set eduSet=userService.getEducateInUser(id); //System.out.println(eduSet); List<Educate> eduList=new ArrayList<Educate>(); Iterator it=eduSet.iterator(); while(it.hasNext()){ Educate eduTemp=(Educate) it.next(); eduList.add(eduTemp); } request.setAttribute("edu", eduList); request.setAttribute("name", username); request.setAttribute("id", id); return "userselecteducate"; } public String insertcourse(){ String[] para=request.getParameterValues("edupara"); String userid=request.getParameter("userid"); if(para.length>0){ User us=userService.getUser(Long.parseLong(userid)); Set<Educate> edus=new HashSet<Educate>(); edus=us.getEducate(); for(String str: para ){ Educate educate=educateService.getForEdu(Long.parseLong(str)); /* Set users=new HashSet<User>(); users=educate.getUser(); System.out.println(users.size()); users.add(us); System.out.println(users.size()); educate.setUser(users); educateService.saveorUpdate(educate); */ edus.add(educate); } us.setEducate(edus); userService.saveOrUpdate(us); } return "insertcourse"; } public String deletecourse(){ String userid=request.getParameter("userid"); User us=userService.getUser(Long.parseLong(userid)); Educate edu=educateService.getForEdu(id); //edu.getUser().remove(us); Set<Educate> educates=new HashSet<Educate>(); educates=us.getEducate(); educates.remove(edu); us.setEducate(educates); userService.saveOrUpdate(us); try { inputStream = new ByteArrayInputStream("1".getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block try { inputStream = new ByteArrayInputStream("0".getBytes("UTF-8")); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } return "ajax-success"; } @Override public void setServletRequest(HttpServletRequest arg0) { // TODO Auto-generated method stub this.request=arg0; } }
EducateAction类:
package com.ssh.action; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.interceptor.ServletRequestAware; import com.opensymphony.xwork2.ActionSupport; import com.ssh.entities.Educate; import com.ssh.entities.User; import com.ssh.service.EducateService; import com.ssh.service.UserService; public class EducateAction extends ActionSupport implements ServletRequestAware{ /** * @author Administrator zhangshitong */ private static final long serialVersionUID = 1L; private Educate educate; private EducateService educateService; private HttpServletRequest request; private InputStream inputStream; private Long id; private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public void setId(Long id) { this.id = id; } public Long getId() { return id; } public InputStream getInputStream() { return inputStream; } public void setEducateService(EducateService educateService) { this.educateService = educateService; } public Educate getEducate() { return educate; } public void setEducate(Educate educate) { this.educate = educate; } public String addeducate(){ educate.setCreatetime(new Date()); System.out.println(educate); educateService.saveorUpdate(educate); return "addeducate"; } public String listeducate(){ List<Educate> list=educateService.getForList(); request.setAttribute("educate", list); return "listeducate"; } public String delete(){ System.out.println("this is id's value:"+id); //Set<User> users=new HashSet<User>(); //users=null; List<User> userList=userService.getForList(); Educate edu=educateService.getForEdu(id); Iterator<User> it=userList.iterator(); while(it.hasNext()){ User userListMember=it.next(); Set<Educate> setEducate=userListMember.getEducate(); if(setEducate.contains(edu)){ setEducate.remove(edu); } userService.saveOrUpdate(userListMember); } //edu.setUser(users); //educateService.saveorUpdate(edu); educateService.delete(id); //educateService.deleteCascade(edu); try { inputStream = new ByteArrayInputStream("1".getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block try { inputStream = new ByteArrayInputStream("0".getBytes("UTF-8")); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } return "ajax-success"; } public String addcourse(){ List<Educate> list=educateService.getForList(); request.setAttribute("educate", list); request.setAttribute("userid", id); return "addcourse"; } @Override public void setServletRequest(HttpServletRequest arg0) { // TODO Auto-generated method stub this.request=arg0; } }
spring的application配置文件(事务管理):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <!-- 导入资源文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置 C3P0 数据源 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> </bean> <!-- 配置 SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <property name="mappingLocations" value="classpath:com/ssh/entities/*.hbm.xml"></property> <property name="hibernateProperties"> <props> <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop> </props> </property> </bean> <!-- 配置 Spring 的声明式事务 --> <!-- 1. 配置 hibernate 的事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 2. 配置事务属性 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- 3. 配置事务切入点, 再把事务属性和事务切入点关联起来 --> <aop:config> <aop:pointcut expression="execution(* com.ssh.dao.*.*(..))" id="txPointcut"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config> </beans>
web.xml配置的过滤器:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>hibernateFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> <init-param> <param-name>sessionFactoryBeanName</param-name> <param-value>sessionFactory</param-value> </init-param> <init-param> <param-name>singleSession</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>