持久化对象的状态
~~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. 项目结构
2. hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">chuck</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.connection.isolation">2</property> <mapping resource="com/baidu/hibernate/app/Person.hbm.xml" /> </session-factory> </hibernate-configuration>3.持久化类 Person.java
package com.baidu.hibernate.app; import java.util.Date; public class Person { private Integer id; private String name; private String interest; private Date birth; 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 String getInterest() { return interest; } public void setInterest(String interest) { this.interest = interest; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public Person() { super(); } public Person(String name, String interest, Date birth) { super(); this.name = name; this.interest = interest; this.birth = birth; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", interest=" + interest + ", birth=" + birth + "]"; } }4. 对象-关系映射 Person.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 package="com.baidu.hibernate.app"> <class name="Person" table="PERSON" > <id name="id" type="java.lang.Integer" > <column name="ID" /> <generator class="hilo" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <property name="interest" type="java.lang.String"> <column name="INTEREST" /> </property> <property name="birth" type="java.util.Date"> <column name="BIRTH" /> </property> </class> </hibernate-mapping>~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~下面是方法测试类~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
① 掌握 persist() 和save() 的区别
② 掌握 get() 和load() 的区别
③ 如何才能避免update 方法不盲目的触发 UPDATE 的SQL语句呢? 如何配置
④ saveOrUpdate() 方法 在什么情况下触发 save() 方法,什么情况下触发update() 方法 。什么样的对象也会被视为游离对象?
⑤ 能否 在删除后即把OID 置为空呢? 如何配置
package com.baidu.hibernate.app; import static org.junit.Assert.*; import java.sql.Connection; import java.sql.SQLException; import java.util.Date; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.jdbc.Work; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestHibernatePerson { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void init(){ Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction(); System.out.println("init"); } @After public void destroy(){ transaction.commit(); System.out.println("WO 在 commit 后"); session.close(); sessionFactory.close(); System.out.println("distroy"); } /** * 1. save() 方法 * ①. 使一个临时对象变为持久化对象 * ②. 为对象分配ID * ③. 在flush 缓存时,会发送一条INSERT 语句 * ④. 在save() 方法之前设置的 ID 是无效的 * ⑤. 持久化对象的ID 是不能被修改的 * * save()方式两条sql 语句: * ①.发送一条select 语句 * ②. 在发送一条update 语句 * * 执行完save()方法后,执行commit 是 会在发送一条insert 语句 */ @Test public void testSave() { Person person = new Person(); person.setName("ZhaoYun"); person.setInterest("music2"); person.setBirth(new Date()); person.setId(20); System.out.println(person); System.out.println("wo zai save 前"); session.save(person); System.out.println("wo zai save 后"); System.out.println(person); System.out.println("WO 在 commit 前"); } /** * persist(): 也会执行INSERT 操作 * * persist() 和save() 的区别: * 当对一个OID 不为null 的对象执行save()方法时,会把该对象以一个新的oid保存到数据库中, * 但是,当执行persist()方法时,则会抛出一个异常 */ @Test public void testPersist(){ Person person = new Person(); person.setName("Zhao"); person.setInterest("music"); person.setBirth(new Date()); // person.setId(30);//如果在最新persist 方法前的这里 这种的 id 属性,这会抛出异常 //org.hibernate.PersistentObjectException: detached entity //passed to persist: com.baidu.hibernate.app.Person session.persist(person); System.out.println("WO 在 commit 前"); } /** * get() 和load() 方法 * 1. 执行get 方法: 会立即查询加载对象, * 2. 执行load 方法, 若不使用该对象,则不会立即执行查询操作,而返回一个代理对象 * * get方法 是立即检索,load 方法是延迟检索。 * * 3. load 方法可能会抛出 org.hibernate.LazyInitializationException: * could not initialize proxy - no Session 异常 * 原因:因为load 是延迟检索,在需要初始化代理对象之前,已经关闭了Session 导致 * * 4. 若数据库中没有对应的记录,session 也没有被关闭: * get 返回 null * load 若不使用该对象的任何属性,则不会抛异常 * 若需要初始化时,则会抛出异常。 */ @Test public void testLoad(){ Person person = (Person) session.load(Person.class, 1); session.close(); System.out.println(person); } @Test public void testGet(){ Person person = (Person) session.get(Person.class, 1); //System.out.println(person); } /** * update: * * 1. 若更新一个持久化对象,不需要显示的调用update() 方法,因为Transaction * 的commit() 方法时,会先执行session 的flush 方法。 * * 2. 更新一个游离对象,需要显式的调用session的update方法,只有显示的调用update() 方法,才会把一个游离对象变成了一个持久化对象 * * 3. 如果调用获取对象后,重新设置了对象的属性,但是属性设置前后没有变化, * 即:数据库中记录id=1 的name属性是 LiuBei, 修改后的name属性 还是LiuBei , * 不论是否显示的调用update() 方法,都发送update 的SQL语句 。 * * 注意: 前提: 当显示的调用了update() 方法时: * ①. 无论要更新的游离对象和数据表中的是否一致,都会发送UPDATE 的 SQL语句。 * * 如何才能避免update 方法不在盲目的触发 UPDATE 的SQL语句呢? * 为了解决这个问题,需要在.hbm.xml 文件的class 中添加 * select-before-update=true(默认为false),但通常不需要设置该属性 * 配置详解见下 A * * ②. 对于游离对象,可以修改其ID, 但若在数据表中没有和修改后的ID对应的记录,且又调用了update() 方法,则会抛出异常 * 若在数据表中有和修改后的ID对应的记录,且又调用了update() 方法,则会更新数据表中 * 和修改后的ID对应的记录 * * ③. 当update() 方法 关联一个游离对象时,如果在Session 的缓存中已经存在相同的OID 的持久化对象,则会抛出异常 * 因为在session 缓存中不能有两个OID 相同的对象同时存在 : 即缓存中不能同时存在两个Id 相同的对象, * 对应同一条数据库的记录 */ @Test public void testUpdate(){ Person person = (Person) session.get(Person.class, 1); transaction.commit();; session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); person.setName("Liu"); session.update(person); System.out.println(person); } /** * Session 的 saveOrUpdate() 方法同时包含了 save() 与 update() 方法的功能 * * 判定对象是否为临时对象的标准 * ① Java 对象的 OID 为 null * ② 映射文件中为 <id> 设置了 unsaved-value 属性, * 并且 Java 对象的 OID 取值与这个 unsaved-value 属性值匹配 * 配置详解见下 B * 注意: * ① 若 OID 不为null, 但是对应该ID的记录,在数据表中又不存在,则会抛出异常:org.hibernate.StaleStateException * ② 了解:OID 值等于id 的 unsaved-value 属性值的对象,也被认为是一个游离对象。 * 详解:Person.hbm.xml 配置中 的 id name="id" type="java.lang.Integer" unsaved-value="200" * 则id=200 的对象会被认为是游离对象 * */ @Test public void testSaveOrUpdate(){ Person person = new Person("Tom", "eating fish", new Date()); person.setId(200); session.saveOrUpdate(person); } /** *delete: 执行删除操作,只要OID 和数据表中一条记录对应,就会准备执行delete 操作 * 若OID 在数据表中没有对应的记录,则会抛出异常 * * 能否 在删除后即把OID 置为空呢? 可以 * Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, * 若把它设为 true, 将改变 delete() 方法的运行行为:delete() 方法会把持久化对象或游离对象的 OID * 设置为 null, 使它们变为临时对象 * * 配置详解见下 C */ @Test public void testDelete(){ Person person = new Person(); //person.setId(851968);//这时person 对象是 当new 的,还未被保存到数据库的数据表中,因此是游离对象 //session.delete(person); person = (Person) session.get(Person.class, 884736);// 这时person 是从数据库总获取的 是一个持久化对象 session.delete(person); } /** * evict:从session 缓存中把指定的持久化对象移除 * */ @Test public void testEvict(){ Person person = (Person) session.get(Person.class, 819200); Person person2 = (Person) session.get(Person.class, 917504); person.setName("AA"); person2.setName("BB"); session.evict(person); } /** * Hibernate 如何调用存储过程 * * 可以通过调用 Work 接口 来完成存储过程: 直接通过 JDBC API 来访问数据库的操作 * * Session 的 doWork(Work) 方法用于执行 Work 对象指定的操作, * 即调用 Work 对象的 execute() 方法. * Session 会把当前使用的数据库连接传递给 execute() 方法. * */ @Test public void testDoWork(){ session.doWork(new Work(){ @Override public void execute(Connection connection) throws SQLException { System.out.println(connection); } }); } }
详解A:需要在.hbm.xml 文件的class 中添加 select-before-update=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 package="com.baidu.hibernate.app"> <!-- 如何才能避免update 方法不在盲目的触发 UPDATE 的SQL语句呢? 为了解决这个问题,需要在.hbm.xml 文件的class 中添加 select-before-update=true(默认为false),但通常不需要设置该属性 --> <class name="Person" table="PERSON" select-before-update=true> <id name="id" type="java.lang.Integer" unsaved-value="200"> <column name="ID" /> <generator class="hilo" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <property name="interest" type="java.lang.String"> <column name="INTEREST" /> </property> <property name="birth" type="java.util.Date"> <column name="BIRTH" /> </property> </class> </hibernate-mapping>
详解B:需要在.hbm.xml 文件的id 中配置
因为unsaved-value 的值配置为多少 ,这会认为 id = 为多少 的对象也是游离对象,
如下:unsaved-value=1 ,这id=1 的对象就会被认为也是游离对象
<?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 package="com.baidu.hibernate.app"> <class name="Person" table="PERSON" select-before-update=true> <!-- OID 值等于id 的 unsaved-value 属性值的对象,也被认为是一个游离对象。 例如:<id name="id" type="java.lang.Integer" unsaved-value="1"> 因为unsaved-value 的值是1 ,这会认为 id = 1 的对象 也是游离对象 --> <id name="id" type="java.lang.Integer" unsaved-value="200"> <column name="ID" /> <generator class="hilo" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <property name="interest" type="java.lang.String"> <column name="INTEREST" /> </property> <property name="birth" type="java.util.Date"> <column name="BIRTH" /> </property> </class> </hibernate-mapping>
详解C:需要在hibernate.cfg.xml 文件中配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">chuck</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.connection.isolation">2</property> <!-- 能否 在删除后即把OID 置为空呢? 可以 Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, 若把它设为 true, 将改变 delete() 方法的运行行为:delete() 方法会把持久化对象或游离对象的 OID 设置为 null, 使它们变为临时对象 --> <property name="hibernate.use_identifier_rollback">true</property> <mapping resource="com/baidu/hibernate/app/Person.hbm.xml" /> </session-factory> </hibernate-configuration>