Hibernate(二)
1.1Hibernate的持久化类状态
1.1.1Hibernate的持久化类状态
- 持久化类:就是一个实体类和数据库表建立了映射关系。
- Hibernate为了方便的管理持久化类,将持久化类分成了三种状态。
- 瞬时态(临时态) transient
- 持久态 persistent
- 脱管态(游离态) detached
1.1.2 三种持久化对象的状态
- 瞬时态 Transient
- 不存在持久化标识OID,尚未与Hibernate的session关联,被认为处于瞬时态,失去引用将被JVM回收。换句话说,持久化对象没有唯一标识OID,没有纳入Session的管理。
- 持久态 Persistent
- 存在持久化标识OID,与当前session有关联,并且相关联的session没有关闭,并且事务没有提交。换句话说,持久化对象有唯一标识OID,已经纳入Session的管理,并且持久化持久态对象具有自动更新数据库的能力。
- 脱管态 Detached
- 存在持久化标识OID,但是没有与当前的session关联,脱管状态改变Hibernate不能检测到。换句话说,持久化对象有唯一的标识OID,没有纳入到session管理。
1.1.3区分三种持久化对象的状态
新建cn.hibernate3.demo1.Book.java类
package cn.hibernate3.demo1; /** * 实体类 * @author love */ public class Book { private Integer id; private String name; private Double price; private String author; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
配置Book.hbm.xml文件
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo1.Book" table="book"> <id name="id" column="id"> <generator class="native"/> </id> <property name="name" column="name" type="java.lang.String"/> <property name="price" column="price" type="java.lang.Double"/> <property name="author" column="author" type="java.lang.String"/> </class> </hibernate-mapping>
配置核心映射文件(hibernate.cfg.xml文件)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 配置数据库的基本信息 --> <!-- 驱动的名称 --> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 访问数据库的url --> <property name="hibernate.connection.url"> jdbc:mysql:///hibernate_day02 </property> <!-- 用户名 --> <property name="hibernate.connection.username">root</property> <!-- 密码 --> <property name="hibernate.connection.password">root</property> <!-- 方言 --> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- C3P0连接池设定--> <!-- 使用c3po连接池 配置连接池提供的供应商--> <property name="connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider </property> <!--在连接池中可用的数据库连接的最少数目 --> <property name="c3p0.min_size">5</property> <!--在连接池中所有数据库连接的最大数目 --> <property name="c3p0.max_size">20</property> <!--设定数据库连接的过期时间,以秒为单位, 如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 --> <property name="c3p0.timeout">120</property> <!--每3000秒检查所有连接池中的空闲连接 以秒为单位--> <property name="c3p0.idle_test_period">3000</property> <!-- 可选配置 --> <!-- 显示SQL --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL --> <property name="hibernate.format_sql">true</property> <!-- hbm:映射 2:to ddl:create drop alter --> <property name="hibernate.hbm2ddl.auto">update</property> <mapping resource="cn/hibernate3/demo1/Book.hbm.xml" /> </session-factory> </hibernate-configuration>
新建HibernateUtils.java类
package cn.utils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; /** * Hibernate抽取工具类 */ public class HibernateUtils { private static Configuration configuration; private static SessionFactory sessionFactory; static{ configuration = new Configuration().configure(); sessionFactory = configuration.buildSessionFactory(); } public static Session openSession(){ return sessionFactory.openSession(); } public static void main(String[] args) { openSession(); } }
运行HibernateUtils工具类,即可发现创建了book表
测试
package cn.hibernate3.demo1; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.utils.HibernateUtils; public class HibernateTest1 { @Test //区分持久化对象的三种装填 public void demo1(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //向数据库中保存一本图书 Book book = new Book();//瞬时态:没有唯一标识OID,没有与session关联 book.setName("Hibernate开发"); book.setAuthor("哈哈"); book.setPrice(65.0); session.save(book);//持久态:有唯一标识OID,与session关联 tx.commit(); session.close(); book.setName("Struts2开发");//脱管态:有唯一标识,没有与session关联 } }
1.1.4三种对象的转换
- 瞬时态:
- 获取瞬时态 Book book = new Book();
- 瞬时态-->持久态 save(book);
- 瞬时态-->脱管态 book.setId(1);
- 持久态
- 获取持久态 Book book = (Book)session.get(Book.class,1);
- get()、load()、find()、iterate();
- 持久态-->瞬时态 delete(book);
- 持久态-->脱管态
- session.close();
- close()/clear()/evict(Object obj)。
- 获取持久态 Book book = (Book)session.get(Book.class,1);
- 脱管态
- 获取脱管态 Book book = new Book();book.setId(1);
- 脱管态-->持久态 session.update(book);
- 脱管态-->瞬时态 book.setId(null);
- 获取脱管态 Book book = new Book();book.setId(1);
1.1.5持久态对象有自动更新数据库的能力
@Test //测试持久态对象有自动更新数据库的能力 public void demo2(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //获取一个持久态对象 Book book = (Book) session.get(Book.class, 1); book.setName("Struts2开发"); tx.commit(); session.close(); }
自动更新数据库的能力依赖了Hibernate的一级缓存。
1.2Hibernate的一级缓存
1.2.1Hibernate的一级缓存
- 什么是缓存?
- 缓存将数据库/硬盘上文件中数据,加入到缓存中(就是内存中的一块的空间),当再次使用的时候,可以直接从内存中获取。
- 缓存的好处?
- 提升程序运行的效率,缓存技术是Hibernate的一个优化的手段。
- Hibernate分为两个基本的缓存?
- 一级缓存:session级别的缓存。一级缓存和session的生命周期一直。自带的,不可卸载。
- 二级缓存:sessionFactory级别的缓存。不是自带的。
1.2.2证明Hibernate一级缓存的存在
@Test //证明一级缓存的存在 public void demo3(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //获取一个持久态对象 Book book = (Book) session.get(Book.class, 1);//发送SQL语句 System.out.println(book); Book book2 = (Book) session.get(Book.class, 1);//不发送SQL语句 System.out.println(book2); tx.commit(); session.close(); }
1.2.3理解session缓存(一级缓存)
- 在session接口的实现中包含了一系列的java集合,这些java集合构成了session缓存,只要session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。
- 当session的save()方法持久化一个对象的时候,该对象被装载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库,没有再查询数据库。
- session能够在某些时间点,按照缓存中对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程被称为刷出缓存(flush)。
- 默认情况下,session在以下时间点刷出缓存:
- 当应用程序调用Transaction的commit()方法的时候,该方法会先刷出缓存,然后再向数据库提交事务。
- 当应用程序执行一些查询操作的时候,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态。
- 调用session的flush()方法。
1.2.4Hibernate一级缓存的快照区
@Test //快照区 public void demo2(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //获取一个持久态对象 Book book = (Book) session.get(Book.class, 1); book.setName("Spring开发"); tx.commit(); session.close(); }
分析:
- 情况一:数据库中的数据和要修改的数据不一致
- 情况二:当数据库中的数据和要修改的数据一致。
【总结】向一级缓存中存入数据的时候,放入一级缓存区和一级缓存快照区,当更新了一级缓存的数据的时候,事务一旦提交,会对比一级缓存和快照区,如果数据一致,不更新;如果数据不一致,就更新数据库。
1.2.5 Hibernate管理一级缓存
- 一级缓存是与session的生命周期相关的,session的生命周期结束,一级缓存就消失了。
- clear()/evict()/flush()/refresh()管理一级缓存。
- clear():清空一级缓存的所有的对象。
- evict(Object obj):清空一级缓存中的某个对象。
- flush():刷出缓存。
- refresh(Object obj):将快照区的数据,覆盖了一级缓存的数据。
1.2.6Hibernate一级缓存刷出时机(了解)
- FlushMode:
- 常量
- ALWAYS:每次查询的时候都会刷出,手动调用flush,事务提交的时候。
- AUTO:默认值,有些查询会刷出,手动调用flush,事务提交的时候。
- COMMIT:在事务提交的时候,手动调用flush的时候。
- MANUAL:只有在手动调用flush才会刷出。
- 严格程序: MANUAL> COMMIT>AUTO>ALWAYS。
- 常量
1.3操作持久化对象的方法
- save()
- 保存一条记录。
- 将瞬时态对象变为持久态对象。
- update()
- 更新一条记录。
- 将脱管态对象变为持久态对象。
- saveOrUpdate()
- 根据对象的状态的不同执行save()或update()方法。
- 如果对象是一个瞬时态对象,执行save()方法。
- 如果对象是一个脱管态对象,执行update()方法。
- 根据对象的状态的不同执行save()或update()方法。
- delete()
- 将持久态对象变为瞬时态对象。
- get()/load()
- 获取一个持久化对象。
1.4Hibernate关联关系的映射
1.4.1实体之间的关系
- 一对多:
- 一个用户,生成多个订单,每一个订单只能属于一个用户。
- 建表的原则:
- 在多的一方创建一个字段,作为外键,指向一的一方的主键。
- 多对多:
- 一个学生,可以选择多门课程,一门课程,可以被多个学生选择。
- 建表的原则:
- 创建第三张表,中间表至少有2个字段,分别作为外键指向多对多双方的主键。
- 一对一:(特殊,应用最少)
- 一个公司只能有一个注册的地址,一个注册地址只能被一个公司所使用。
- 建表的原则:
- 唯一外键:
- 一对一的双方,假设一方是多的关系,需要在多的一方创建一个字段作为外键,指向一的一方的主键。但是,在外键上添加一个unique。
- 主键对应:
- 一对一的双方,通过主键进行关联。
- 唯一外键:
1.4.2Hibernate中一对多的配置
- 创建实体
- 创建客户实体
package cn.hibernate3.demo2; import java.io.Serializable; import java.util.HashSet; /** * 客户实体 */ import java.util.Set; public class Customer implements Serializable{ private Integer cid; private String cname; //一个客户有多个订单 private Set<Order> orders = new HashSet<Order>(); public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } }
- 订单实体
package cn.hibernate3.demo2; import java.io.Serializable; /** * 订单实体 */ public class Order implements Serializable{ private Integer oid; private String addr; //订单属于某一个客户 private Customer customer ; public Integer getOid() { return oid; } public void setOid(Integer oid) { this.oid = oid; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
- 配置映射文件
- Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Customer" table="customer"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set标签中的name表示关联对象的属性名称 --> <set name="orders" > <!-- key标签中的column用来一对多的多的一方的外键 --> <key column="cno"/> <!-- 配置一个one-to-many --> <one-to-many class="cn.hibernate3.demo2.Order"/> </set> </class> </hibernate-mapping>
- Order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Order" table="orders"> <!-- 配置唯一标识 --> <id name="oid" column="oid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="addr" column="addr" type="java.lang.String"/> <!-- 建立映射 --> <!-- many-to-one标签 属性: name:关联对象的属性名称。 column:表中外键的名称。 class:关联对象的全路径。 --> <many-to-one name="customer" column="cno" class="cn.hibernate3.demo2.Customer"></many-to-one> </class> </hibernate-mapping>
- 将映射文件放到核心配置文件中
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 配置数据库的基本信息 --> <!-- 驱动的名称 --> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 访问数据库的url --> <property name="hibernate.connection.url"> jdbc:mysql:///hibernate_day02 </property> <!-- 用户名 --> <property name="hibernate.connection.username">root</property> <!-- 密码 --> <property name="hibernate.connection.password">root</property> <!-- 方言 --> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- C3P0连接池设定--> <!-- 使用c3po连接池 配置连接池提供的供应商--> <property name="connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider </property> <!--在连接池中可用的数据库连接的最少数目 --> <property name="c3p0.min_size">5</property> <!--在连接池中所有数据库连接的最大数目 --> <property name="c3p0.max_size">20</property> <!--设定数据库连接的过期时间,以秒为单位, 如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 --> <property name="c3p0.timeout">120</property> <!--每3000秒检查所有连接池中的空闲连接 以秒为单位--> <property name="c3p0.idle_test_period">3000</property> <!-- 可选配置 --> <!-- 显示SQL --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL --> <property name="hibernate.format_sql">true</property> <!-- hbm:映射 2:to ddl:create drop alter --> <property name="hibernate.hbm2ddl.auto">update</property> <mapping resource="cn/hibernate3/demo2/Customer.hbm.xml" /> <mapping resource="cn/hibernate3/demo2/Order.hbm2.xml" /> </session-factory> </hibernate-configuration>
- 运行HibernateUtils.java类
1.4.3Hibernate中一对多级联保存
@Test //向客户表插入一个客户,在订单表中插入两个订单 public void demo1(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //定义客户 Customer customer = new Customer(); customer.setCname("郭浩"); //定义订单 Order order1 = new Order(); order1.setAddr("新疆"); Order order2 = new Order(); order2.setAddr("江苏"); //建立关系 customer.getOrders().add(order1); customer.getOrders().add(order2); order1.setCustomer(customer); order2.setCustomer(customer); session.save(customer); session.save(order2); session.save(order1); tx.commit(); session.close(); }
1.4.4Hibernate中一对多的级联保存
- 从1.4.3我们让一方和多方对象产生关联关系,并保存一方和多方对象,那么我们是否只保存一方呢?
@Test //向客户表插入一个客户,在订单表中插入两个订单 public void demo2(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //定义客户 Customer customer = new Customer(); customer.setCname("郭浩"); //定义订单 Order order1 = new Order(); order1.setAddr("新疆"); Order order2 = new Order(); order2.setAddr("江苏"); //建立关系 customer.getOrders().add(order1); customer.getOrders().add(order2); //保存的时候只保存一方 session.save(customer); tx.commit(); session.close(); }
此时,会出现异常。
上面的异常的意思就是对象引用了一个没有保存的瞬时态的对象,换句话说,就是customer对象是持久态的,而order1和order2是瞬时态的对象,Hibernate不允许持久态对象关联瞬时态对象。
- 没有办法了吗?不是,我们需要使用Hibernate提供的级联保存。
- 级联:操作当前对象的时候,关联的对象如何处理。
- 级联的方向性:
- 保存客户的时候,选择级联订单。
- 保存订单的时候,选择级联客户。
- cascade="save-update"。
@Test //级联保存 保存客户的时候,级联订单 //<set>标签上是客户的关联订单对象的集合,所以在<set>上配置一个属性:cascade public void demo3(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //定义客户 Customer customer = new Customer(); customer.setCname("郭浩"); //定义订单 Order order1 = new Order(); order1.setAddr("新疆"); Order order2 = new Order(); order2.setAddr("江苏"); //建立关系 customer.getOrders().add(order1); customer.getOrders().add(order2); //保存的时候只保存一方 session.save(customer); tx.commit(); session.close(); }
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Customer" table="customer"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set标签中的name表示关联对象的属性名称 --> <set name="orders" cascade="save-update"> <!-- key标签中的column用来一对多的多的一方的外键 --> <key column="cno"/> <!-- 配置一个one-to-many --> <one-to-many class="cn.hibernate3.demo2.Order"/> </set> </class> </hibernate-mapping>
- 如何我从多的一方呢?
- 在<many-to-one>上配置cascade="save-update"
@Test public void demo4(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //定义客户 Customer customer = new Customer(); customer.setCname("郭浩"); //定义订单 Order order1 = new Order(); order1.setAddr("新疆"); Order order2 = new Order(); order2.setAddr("江苏"); //建立关系 customer.getOrders().add(order1); customer.getOrders().add(order2); order1.setCustomer(customer); order2.setCustomer(customer); session.save(order1); session.save(order2); tx.commit(); session.close(); }
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Order" table="orders"> <!-- 配置唯一标识 --> <id name="oid" column="oid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="addr" column="addr" type="java.lang.String"/> <!-- 建立映射 --> <!-- many-to-one标签 属性: name:关联对象的属性名称。 column:表中外键的名称。 class:关联对象的全路径。 --> <many-to-one cascade="save-update" name="customer" column="cno" class="cn.hibernate3.demo2.Customer"></many-to-one> </class> </hibernate-mapping>
1.4.5Hibernate中一对多级联删除
默认情况下,将外键设置为null,删除数据记录。
@Test //默认情况下,将外键设置为null,然后删除记录 public void demo5(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); session.delete(customer); tx.commit(); session.close(); }
- 添加cascade="delete"。
@Test //级联删除 public void demo5(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); session.delete(customer); tx.commit(); session.close(); }
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Customer" table="customer"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set标签中的name表示关联对象的属性名称 --> <set name="orders" cascade="delete"> <!-- key标签中的column用来一对多的多的一方的外键 --> <key column="cno"/> <!-- 配置一个one-to-many --> <one-to-many class="cn.hibernate3.demo2.Order"/> </set> </class> </hibernate-mapping>
1.4.6Hibernate中级联的取值
- none:不使用级联。
- save-update:保存或更新的时候使用级联。
- delete:删除的时候使用级联。
- all:除了孤儿删除以外的所有级联。
- delete-orphan:孤儿删除。
- 仅限于一对多的情况,只有一对多时候,才有父子表(主从表),认为一的一方是父表,多的一方是子表。
- 当一个客户与某个订单解除了关系,将外键设置为null。订单没有了所属的客户,相当于一个孩子没有父亲,就相当于一个孤儿,那么Hibernate就会将这类没有外键的记录删除。
- all-delete-orphan:包含了孤儿删除的所有的级联。
演示:孤儿删除的情况,默认情况下。
@Test //孤儿删除,默认情况下,是将外键设置为null。 public void demo6(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); Order order = (Order) session.get(Order.class, 1); customer.getOrders().remove(order); tx.commit(); session.close(); }
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Customer" table="customer"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set标签中的name表示关联对象的属性名称 --> <set name="orders" > <!-- key标签中的column用来一对多的多的一方的外键 --> <key column="cno"/> <!-- 配置一个one-to-many --> <one-to-many class="cn.hibernate3.demo2.Order"/> </set> </class> </hibernate-mapping>
- 配置孤儿删除。
@Test //孤儿删除 public void demo6(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); Order order = (Order) session.get(Order.class, 1); customer.getOrders().remove(order); tx.commit(); session.close(); }
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Customer" table="customer"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set标签中的name表示关联对象的属性名称 --> <set name="orders" cascade="delete-orphan"> <!-- key标签中的column用来一对多的多的一方的外键 --> <key column="cno"/> <!-- 配置一个one-to-many --> <one-to-many class="cn.hibernate3.demo2.Order"/> </set> </class> </hibernate-mapping>
1.4.7双向维护产生多余SQL及inverse配置
@Test //双向关联,产生多余的SQL public void demo7(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); Order order = (Order) session.get(Order.class, 2); customer.getOrders().add(order); order.setCustomer(customer); tx.commit(); session.close(); }
- 分析
- 所以,双向维护,意味着双方都有外键的维护能力,那怎么办呢?必须让其中的一方放弃外键的维护权。
- 添加属性inverse="true"。
- inverse:是反转的意思,即我不维护,就是我放弃外键的维护权。
- 一般情况下,是一方放弃的,多方维护的。
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo2.Customer" table="customer"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set标签中的name表示关联对象的属性名称 --> <set name="orders" inverse="true"> <!-- key标签中的column用来一对多的多的一方的外键 --> <key column="cno"/> <!-- 配置一个one-to-many --> <one-to-many class="cn.hibernate3.demo2.Order"/> </set> </class> </hibernate-mapping>
【注意】
- cascade:操作关联对象。
- inverse:控制外键的维护。
1.4.8Hibernate中多对多的配置
- 建立实体类
- Student.java
package cn.hibernate3.demo3; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Student implements Serializable{ private Integer sid; private String sname; private Set<Course> courses = new HashSet<Course>(); public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } }
- Course.java
package cn.hibernate3.demo3; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Course implements Serializable{ private Integer cid; private String cname; private Set<Student> students = new HashSet<Student>(); public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
- 建立映射
- Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo3.Student" table="student"> <!-- 配置唯一标识 --> <id name="sid" column="sid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="sname" column="sname" type="java.lang.String"/> <!-- 建立映射 --> <!-- <set>标签中的name:对应学生中课程集合的名称 table对应的是中间表的名称 --> <set name="courses" table="student_course"> <!-- <key>标签对应的是中间表中的外键 --> <key column="sid"/> <!-- <many-to-many>对应的是另一方的类的全路径,column:另一方在中间表中外键的名称 --> <many-to-many class="cn.hibernate3.demo3.Course" column="cid" /> </set> </class> </hibernate-mapping>
- Course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo3.Course" table="course"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <set name="students" table="student_course"> <key column="cid"/> <many-to-many class="cn.hibernate3.demo3.Student" column="sid"/> </set> </class> </hibernate-mapping>
- 将映射文件加载到核心配置文件中
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 配置数据库的基本信息 --> <!-- 驱动的名称 --> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 访问数据库的url --> <property name="hibernate.connection.url"> jdbc:mysql:///hibernate_day02 </property> <!-- 用户名 --> <property name="hibernate.connection.username">root</property> <!-- 密码 --> <property name="hibernate.connection.password">root</property> <!-- 方言 --> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- C3P0连接池设定--> <!-- 使用c3po连接池 配置连接池提供的供应商--> <property name="connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider </property> <!--在连接池中可用的数据库连接的最少数目 --> <property name="c3p0.min_size">5</property> <!--在连接池中所有数据库连接的最大数目 --> <property name="c3p0.max_size">20</property> <!--设定数据库连接的过期时间,以秒为单位, 如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 --> <property name="c3p0.timeout">120</property> <!--每3000秒检查所有连接池中的空闲连接 以秒为单位--> <property name="c3p0.idle_test_period">3000</property> <!-- 可选配置 --> <!-- 显示SQL --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL --> <property name="hibernate.format_sql">true</property> <!-- hbm:映射 2:to ddl:create drop alter --> <property name="hibernate.hbm2ddl.auto">update</property> <mapping resource="cn/hibernate3/demo3/Student.hbm.xml" /> <mapping resource="cn/hibernate3/demo3/Course.hbm.xml" /> </session-factory> </hibernate-configuration>
【注意】:映射文件的理解
1.4.9Hibernate中多对多的保存
- 测试类
@Test //保存学生和课程,为学生选择一些课程 public void demo1(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //创建学生 Student student1 = new Student(); student1.setSname("张三"); Student student2 = new Student(); student2.setSname("李四"); //创建课程 Course course1 = new Course(); course1.setCname("java语言"); Course course2 = new Course(); course2.setCname("Android语言"); //张三选择1号和2号课程 student1.getCourses().add(course1); student1.getCourses().add(course2); course1.getStudents().add(student1); course2.getStudents().add(student1); //李四选择1号课程 student2.getCourses().add(course1); course1.getStudents().add(student2); //保存记录 session.save(student1); session.save(student2); session.save(course1); session.save(course2); tx.commit(); session.close(); }
但是,这样会抛出异常。
所以,多对多的配置中,必须有一方放弃主键维护。比如学生放弃主键维护的权利。
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo3.Student" table="student"> <!-- 配置唯一标识 --> <id name="sid" column="sid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="sname" column="sname" type="java.lang.String"/> <!-- 建立映射 --> <!-- <set>标签中的name:对应学生中课程集合的名称 table对应的是中间表的名称 --> <set name="courses" table="student_course" inverse="true"> <!-- <key>标签对应的是中间表中的外键 --> <key column="sid"/> <!-- <many-to-many>对应的是另一方的类的全路径,column:另一方在中间表中外键的名称 --> <many-to-many class="cn.hibernate3.demo3.Course" column="cid" /> </set> </class> </hibernate-mapping>
最新文章
- 设计模式(七)适配器模式(Adapter Pattern)
- (译)详解javascript立即执行函数表达式(IIFE)
- 推荐几个的chorme的扩展程序
- Android Studio系列教程五--Gradle命令详解与导入第三方包
- Human Gene Functions(poj 1080)
- 【BZOJ】1049: [HAOI2006]数字序列(lis+特殊的技巧)
- php 安装composer
- JSOI2008 最小生成树计数
- elasticsearch基础概念
- 提高java编程质量 - (四)i++ 和 ++i 探究原理
- selenium自动化测试配置工具整理
- [Codeforces]813F Bipartite Checking
- android M Launcher之LauncherModel (一)
- Spring Cloud(Dalston.SR5)--Config 集群配置中心-刷新配置
- tomcat设置开机自动启动
- C#事件の事件聚合器
- sort.js
- wpf(使用定时器)使用定时器操作UI界面
- 峰Spring4学习(6)spring AOP的应用例子
- POJ 3481 Double Queue(set实现)