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>
Hibernate(二)的更多相关文章
- Hibernate二次学习一----------Hibernate简单搭建
因为博客园自带的markdown不太好用,因此所有markdown笔记都使用cmd_markdown发布 Hibernate二次学习一----------Hibernate简单搭建: https:// ...
- hibernate(二)一级缓存和三种状态解析
序言 前一篇文章知道了什么是hibernate,并且创建了第一个hibernate工程,今天就来先谈谈hibernate的一级缓存和它的三种状态,先要对着两个有一个深刻的了解,才能对后面我要讲解的一对 ...
- Hibernate(二)——POJO对象的操作
POJO对象其实就是我们的实体,这篇博客总结一下框架对POJO对象对应数据库主键的生成策略,和一些对POJO对象的简单增删改查的操作. 一,Hibernate框架中主键的生成策略有三种方式: 1,数 ...
- Spring整合Hibernate 二 - 声明式的事务管理
Spring大战Hibernate之声明式的事务管理 Spring配置文件: 添加事务管理类的bean: <bean id="txManager" class="o ...
- Hibernate(二)
性能分析 抓取策略 研究对象 研究怎么样提取集合的,该策略应该作用与set元素上 研究从一的一方加载多的一方 案例 查询cid为1的班级的所有的学生 明:通过一条sql语句:左外链接,把classes ...
- Hibernate(二)__简单实例入门
首先我们进一步理解什么是对象关系映射模型? 它将对数据库中数据的处理转化为对对象的处理.如下图所示: 入门简单实例: hiberante 可以用在 j2se 项目,也可以用在 j2ee (web项目中 ...
- Hibernate二 映射 注解 一级缓存
Hibernate映射1.@Entity 被该注解修饰的POJO类是一个实体,可以用name属性指定该实体类的名称,系统默认以该类的类名作为实体类的名称.2.@Table 指定持久化类所映射的表,它的 ...
- Hibernate 二(一级缓存,多表设计之一对多)
1 对象状态与一级缓存 1.1 状态介绍 l hibernate 规定三种状态:瞬时态.持久态.脱管态 l 状态 瞬时态:transient,session没有缓存对象,数据库也没 ...
- ssh架构之hibernate(二)进阶学习
1.JPA入门 JPA的认识:JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中Java持久层AP ...
- Hibernate(二)持久化对象的状态
简介 以前学习Hibernate的笔记,整理一下便发出来了,防止弄丢.有错误的话麻烦各位留言评论,感激不尽. 持久化类 Hibernate完成了从面向对象模型表示的对象至关系模型表示的数据结构的映射, ...
随机推荐
- linux vim编辑
常用vim操作自我总结,大神请回避: vim 打开退出保存 打开:直接打开 vim practice_1.txt也可以先打开vim,后:e 文件路径 保存::w 文件名或者退出保存:wq! 普通模式下 ...
- 想从事IT行业的你,一定看看这篇文章
很多想从事IT行业的小伙伴都会问: 我该如何学习技术? 我应该选择什么样的方向来深入学习并以此来就业? 如何证明自己的技术很牛? 什么是程序员的核心竞争力? 如何成为一名优秀的工程师? 对于这些疑问, ...
- ReIn
package JBJADV003; import java.io.*;import java.util.Scanner; public class ReIn { /** * @param args ...
- JavaMail 邮件开发
(api + 配置) 开发中,邮件的应用? -à 注册,填写生日: 后期系统会自动发送生日祝贺 -à 发货,发货提醒!邮件提醒! 邮件: 1. 发邮件:[程序中如何发邮件!] 2. 收邮件:[ ...
- Java 时钟
<!DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8" ...
- es6知识总结--3
es6知识总结--3 es6对咱们es3,es5的数据类型进行了升级下边说新APIs! js数据类型有Number.String .oject.Boolean.Null.Undefined六种数据类型 ...
- java循环、数组练习
System.out.println("请输入学生个数"); int a=sc.nextInt();//定义一个变量说明学生的数量 int max=0; int[] scores= ...
- virtualbox 安装centos系统,设置双网卡实现虚拟机上网及主宿互访
写在前面:前两天想玩linux,在VMware中装了centos,进入系统后发现连不上网,搜了下教程,/etc/sysconfig/network-scripts/目录下没有 ifcfg-e*的文件 ...
- IScroll那些事——内容不足时下拉刷新
之前项目中的列表是采用的IScroll,但是在使用IScroll有一个问题就是:当内容不足全屏的时候,是木有办法往下拉的,这样就达不到刷新的目的了.[这是本人工作中遇到的,具体例子具体分析,这里只作一 ...
- 在ASP.NET MVC中利用Aspose.cells 将查询出的数据导出为excel,并在浏览器中下载。
正题前的唠叨 本人是才出来工作不久的小白菜一颗,技术很一般,总是会有遇到一些很简单的问题却不知道怎么做,这些问题可能是之前解决过的.发现这个问题,想着提升一下自己的技术水平,将一些学的新的'好'东西记 ...