持久化作用
持久化封装了数据访问的细节,为业务逻辑层提供了面向对象的API.完善的持久化层应该达到 代码重用性好,可完成所有数据访问工作,具有独立性,当持久层发生变化时,不影响上层实现Hibernate的持久化对象具体作用
对象与数据库同步,使用对象操作数据库。 实体域对象在内存中创建后,不能永久存在。将实体域对象永久保存起来,就是持久化的过程。通常只有实体域对象需要持久化,过程域对象和事件域对象一般不需要持久化。广义持久化指增、删、改、查。在持久化类的过程
实际就是操作get set方法Hibernate的唯一性标识 (1)Java按地址区分同一个类的不同对象. (2)关系数据库用主键区分同一条记录. (3)Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系。对象的OID和数据库的表的主键对应。为保证OID的唯一性,应该让Hibernate来为OID赋值。
自然主键和代理主键
自然主键:具有业务含义的字段,如name 代理主键:不具有业务含义的字段,如idincrement
先获取当前的id的最大值,再对添加的条目id做+1操作,时hibernate维护自增的一种方式 特点:跨数据库,不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。identity
identity是由数据库自己生成的,但这个主键必须设置为自增长, 相当于再创表时设置 autoincrement 只能用在支持自动增长的字段数据库中使用,如MySQL。不支持oricalnative
特点:根据数据库自动选择,项目中如果用到多个数据库时,可以使用这种方式,使用时需要设置表的自增字段或建立序列,建立表等。 例如MySQL使用identity,Oracle使用sequenceuuid
UUID:Universally Unique Identifier,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字,标准的UUID格式为: 特点:uuid长度大,占用空间大,跨数据库,不用访问数据库就生成主键值第一步:配置两个持久化对象
//顾客类 public class Customer implements Serializable { private Integer id; private String name; //订单类 public class Order implements Serializable { private Integer id; private String name; private String orderNO; private Customer customer;第二步:建立2个持久化类对应的映射文件Customer.hbm.xml和Order.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?> <hibernate-mapping> <class name="com.exampe.domain.Customer" table="t_customer"> <id name="id" type="integer"> <column name="id"></column> <generator class="identity"></generator> </id> <property name="name" type="string"> <column name="name"></column> </property> </class> </hibernate-mapping> <class name="com.exampe.domain.Order" table="t_order"> <id name="id" type="integer"> <column name="id"></column> <generator class="identity"></generator> </id> <property name="name" type="string"> <column name="name"></column> </property> <!-- 创建一对多关联 name代表属性值 class 关联类的全路径名称 customer_id指的是外键 --> <many-to-one name="customer" class="com.exampe.domain.Customer" column="cid" > </many-to-one>第三步 存储两个表的数据信息
//配置两个类的关联 configuration.configure(); configuration.addClass(Customer.class); configuration.addClass(Order.class); //获取session Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); //创建customer对象,并设置内容 Customer customer = new Customer(); customer.setName("李四"); //创建订单对象 Order order = new Order(); order.setName("滑板鞋2"); order.setOrderNO("N02"); //进行对象关联 order.setCustomer(customer); //存储到session中 session.save(order); session.save(customer); //提交事务 transaction.commit(); session.close();双项关联:既建立从Order到Customer的多对一关联, 又建立从Customer到Order的一对多关联。
第一步 定义双向关联的持久化对象 public class Customer implements Serializable { private Integer id; private String name; private Set orders = new HashSet();
public class Order implements Serializable { private Integer id; private String name; private String orderNO; private Customer customer;第二步 配置对应的xml信息
<class name="com.exampe.domain.Customer" table="t_customer"> <id name="id" type="integer"> <column name="id"></column> <generator class="identity"></generator> </id> <property name="name" type="string"> <column name="name"></column> </property> <--! 这里创建一个set集合,定义名称以及关联的表明,指定对应的时t_order中哪一个键--> <set name="orders" table="t_order"> <key> <column name="cid"></column> </key> <one-to-many class="com.exampe.domain.Order" /> </set>第三步 代码测试
// 获取session Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Order order = new Order(); order.setName("电脑"); order.setOrderNO("N03"); Customer customer = new Customer(); customer.setName("李四"); // 顾客关联订单 customer.getOrders().add(order); // 订单关联顾客 order.setCustomer(customer); session.save(order); session.save(customer); // 提交事务 transaction.commit(); session.close();cascade:级联:
save-update:级联保存更新
inverse
在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系 在没有设置 inverse=true 的情况下,父子两边都维护父子关系 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多) 在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句 结论: 1.在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,这可以提高性能。 2.在建立两个对象的关联时,应该同时修改关联两端的相应属性: customer.getOrders().add(order); order.setCustomer(customer); 这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性: Customer.getOrders().remove(order); Order.setCustomer(null);save方法能够将临时状态转化程持久化状态 使用get或者load方法获取的对象肯定是持久化状态 位于session缓存中
oid 不为空 位于session缓存中 当执行查询时,持久化对象和数据库中的相关记录 session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库中的内容 在同一个session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象。new 对象时为临时状态
在使用代理主键的情况下,oid通常为null, 不处于session的缓存中 在数据库中没有对应的记录当调用session的delete方法时,对象状态就变成了删除状态
OID 不为 null 从一个 Session实例的缓存中删除 Session 已经计划将其从数据库删除, Session 在清理缓存时, 会执行 SQL delete 语句, 删除数据库中的对应记录 一般情况下, 应用程序不该再使用被删除的对象当调用session.close();session的缓存被清空,缓存中所有对象都会变成游离对象,生命周期结束
session的evict()方法能够从缓存中删除一个持久化对象,从而时该持久化对象变成游离对象,当session的缓存中保存了大量的持久化对象,会消耗很多内存空间,为了提高性能,调用evict方法,从缓存中删除一些对象
oid不为null 不处于session缓存中 一般有持久化对象转化成游离对象代码示例
@Test public void add() { // 获取session Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); // 创建customer对象,并设置内容 // 1 刚刚创建对象---临时状态--- Customer customer = new Customer(); customer.setName("赵六"); // 存储到session中 // 2将对象保存到session中----持久化状态----, session.save(customer); // 3 删除 对象 删除状态 session.delete(customer); // 提交事务 transaction.commit(); session.close(); //4 当session关闭之后,对象变成游离状态 System.out.print(customer.getId()+"-----"); }持久化对象和游离状态转换
//持久化状态和游离状态之间转换(了解) Session s = sf.openSession(); Transaction tr = s.beginTransaction(); Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句 s.evict(c1);//将持久对象转换成游离对象 s.update(c1);//将游离对象转换成持久对象 Customer c2 = (Customer)s.get(Customer.class, 1); tr.commit();//先执行s.flush,然后再提交事务 s.close();save()方法:
将临时对象转换持久对象update()方法:将游离对象转换持久对象
注意:当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常evict()方法:
将持久对象转换成游离对象saveOrUpdate()方法:
存在save方法和update方法的特征get() load()
都可以根据给定的 OID 从数据库中加载一个持久化对象 区别: 1:当数据库中不存在与 OID 对应的记录时, load() 方法抛出 ObjectNotFoundException 异常, 而 get() 方法返回 null 2:两者采用不同的延迟检索策略立即检索: 立即加载检索方法指定的对象,对应配置文件中lazy=false
延迟检索: 延迟加载检索方法指定的对象,对应配置文件中lazy=true
测试立即检索和延迟检索的代码,注意只有load()方法进行查询的时候,会产生延迟检索。 模型类设置为final修饰将无法生成代理对象
get()方法:立即检索,只要调用get方法查询数据库,马上执行sql语句,查询对应的结果 load()方法:会产生延迟检索,调用load()方法的时候,不会马上查询数据库,而是产生一个代理对象, 代理对象中只初始化对象的OID,不会初始化其他的属性值 而是调用其他属性值的时候,例如c.getName(),此时才会组织sql语句,查询数据库,返回对应的结果 load()方法如何立即检索呢? 只需要更改 class name="xxx" table="xxx" lazy="false"> 使用load方法可以对性能进行优化,如果只想获取oid的值(比如删除),此时会采用load()方法比get()更适合,因为不需要查询数据库,就可以获取oidload方式访问游离状态的对象
org.hibernate.LazyInitializationException: could not initialize proxy - no Session Customer c = (Customer)s.load(Customer.class, 1); if(!Hibernate.isInitialized(c)){ System.out.println(c.getClass()); System.out.println(c.getId()); System.out.println(c.getName()); 调用Hibernate.initialize(),将代理对象放置到方法中,此时就会查询数据库,返回对应的真实对象 Hibernate.initialize(c); System.out.println(c.getName()); }检索策略
立即检索
优点:不管对象处于持久化状态还是游离状态,都方便使用 缺点:select 会直接将所有字段都查询出来 关联查询时,可能会查出没必要查询出的对象,浪费内存 优先使用场景:应用程序需要立即访问到对象延迟检索
优点:可以自行决定查询哪些对象,降低内存消耗 缺点:应用程序使用游离状态的对象时,必须保证其已经被初始化 优先使用场景:应用程序不需要立即访问到对象的属性,比如只想获取到对象的oid,做删除操作。 一对多或者多对多关联时在hibernate的配置文件中,设置session与本地线程绑定的代码
<property name="hibernate.current_session_context_class">thread</property> 测试Session与本地线程绑定 public void testSessionThread(){ // Session s1 = sf.openSession(); // Session s2 = sf.openSession(); Session s1 = sf.getCurrentSession(); Session s2 = sf.getCurrentSession(); System.out.println(s1==s2);//false:此时说明2个Session不是一个对象;true:2个Session是一个对象(与本地线程绑定) // s1.close(); // s2.close(); } 这里:不再调用sessionFactory.openSession().而是调用sessionFactory. getCurrentSession().获取session对象.从当前的线程提取session, * 当前线程如果存在session对象,取出直接使用 * 当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定