2月5日——培训第64天

    技术2022-05-11  62

    简单说一下昨天没说完的东西,就是弓燕军做出来的那道题目

    orders表:主键自增

    order_id   integer主键order_date   Timestampship_address  varchar(200)curr_item_id  integer(通过这个编号得知当前订单的订单项到第几号了,     从而决定下一个订单项是第几号,默认值是0)

    line_items表:该表中的主键不需要自增

    item_id    integer主键order_id   integer主键和外键product_name  varchar(100)quantity   integer

    二者关系是一对多的关系,之间关系名称为order_item,外键关联。

    新建java工程,导入BuildingPath……

    创建类:package vo;

    public class Order{ private int id ; private Timestamp orderDate ; private String shipAddress ;  private int currItemId = 1 ;//主动设置itemId private Set lineItems = new HashSet() ;

     public int getCurrItemId() {  return currItemId ++ ;  } //getters and setters //需要重新实现equals和hasCode方法, //只需要使用eclipse3.2功能中的create equals and hasCode即可。};

    package vo ;//主键类:public class LineItemId implements Serializable{ private int itemId ; private Order order ; //代表关系

     //getters and setters  public boolean equals(Object obj){}

     public int hasCode(){}}

    package vo ;

    public class LineItem{ private LineItemId id ; private String productName ; private int quantity ; //getters and setters}

    映射文件:

    <hibernate-configuration>  <session-factory>   <property name="dialect">org.hibernate.dialect.MySQLDialect</property>   <property name="connection.driver_class">com.mysql.jdbc.Driver</property>   <property name="connection.url">jdbc:mysql:///netstore?useUnicode=true&        characterEncoding=gbk</property>   <property name="connection.username">root</property>   <property name="connection.password">1234</property>      <property name="show_sql">true</property>   <property name="format_sql">true</property>   <property name="current_session_context_class">thread</property>     </session-factory></hibernate-configuration>---------------------------------------------------------------------------

    <hibernate-mapping auto-import="true" package="vo"> <class name="Order" table="orders">  <id name="id" column="order_id">   <generator class="identity"/>  </id>  <property name="orderDate" column="order_date"/>  <property name="shipAddress" column="ship_address"/>  <property name="currItemId" column="curr_item_id" />  <set name="lineItems">   <key column="order_id"/>   <one-to-many class="LineItem"/>  </set>  <!--  <list name="lineItems">   <key column="order_id" />   <list-index column="item_id" base="1" />   <one-to-many class="LineItem" />  </list>  -->  可以通过注释中的list配置让item_id自动索引,这就不需要从order实体类中  去获取当前的订单项的号了,通过list索引自己获取就可以了。如果用这段注释  的话,再更改一下相应的实体类,将Set改成List就可以了 </class>  <class name="LineItem" table="lineitems">  <composite-id name="id" class="LineItemId">   <key-property name="itemId" column="item_id" />   <key-many-to-one name="order" column="order_id" />  </composite-id>  <property name="productName" type="product_name"/>  <property name="quantity" column="quantity"/>  </class></hibernate-mapping>

    ----------------------------------------------------------

    public class Demo {

     public static void main(String[] args) {  SessionFactory factory = new Configuration()        .configure()        .addClass(Order.class)        .buildSessionFactory();  Session session = null;    Order order = new Order() ;  order.setOrderDate(new Timestamp(System.currentTimeMillis()));  order.setShipAddress("new ship address");

      LineItemId itemId = new LineItemId();//需要生成订单项的主键  itemId.setItemId(order.getCurrItemId());  //注意上面这句话是为了设置订单项id的,但是如果我们在配置文件的Order里面使用了  //list而不是set的话,订单项的id就由list索引设置了,那么上面这句话就不需要了。  //也就是说,我们上面的Order实体类中维护的currItemId,它的getter方法每次将  //这个currItemId加1,就是为了设置这个订单项id,既然现在我们设置了list的话,这  //个东西就没有必要了……  itemId.setOrder(order);

      LineItem line = new LineItem() ;  line.setId(itemId);  line.setProductName("new product") ;  line.setQuantity(12) ;

      try {   session = factory.getCurrentSession();   session.beginTransaction();         session.save(order); //保存订单   order.getLineItems().add(line);   session.save(line);         session.getTransaction().commit();  }   catch (HibernateException e)   {   if(session != null && session.getTransaction() != null)   {    session.getTransaction().rollback();   }   e.printStackTrace();  }  finally  {   if(session != null)   {    session.close();    session = null;   }  }  factory.close();

     }

    }=============================================================================

     

    ================================================================================================================================================将上面的改成组成的方式:

    <hibernate-mapping auto-import="true" package="vo"> <class name="Order" table="orders">  <id name="id" column="order_id">   <generator class="identity"/>  </id>  <property name="orderDate" column="order_date"/>  <property name="shipAddress" column="ship_address"/>  <property name="currItemId" column="curr_item_id" />  //值集合,不需要将lineItems作为class单独映射了。  <list name="lineItems" table="line_items">   <key column="order_id" />   <list-index column="item_id" base="1" />   <composite-element class="LineItem">    <property name="productName" column="product_name"/>    <property name="quantity" column="quantity"/>   </composite-element>  </list>    <!--   //如果使用Set的话必须这么配置,同时记住主动设置itemId,就像上午  //做的一样。   <Set name="lineItems" table="line_items">    <key column="order_id" />       <composite-element class="LineItem">     <property name="currItemId" column="item_id">     <property name="productName" column="product_name"/>     <property name="quantity" column="quantity"/>    </composite-element>   </Set>  -->

     </class> </hibernate-mapping>

    //让订单处于持久状态,然后直接添加line订单项对象就可以了,订单项现在不再是独立的//实体,所以写成session.save(line)就不行了。session.save(order);order.getLineItems().add(line);

    ==========================================================================

    HQL支持以下连接方式:1、inner join(内连接),简写inner2、left outer join(左外连接),简写left join3、right outer join(右外连接),简写right join4、full join (not usually useful)与标准SQL用法完全一致

    from Cat as cat inner join cat.mate as mate   left outer join cat.kittens as kittenfrom Cat as cat    left join cat.mate.kittens as kittensfrom Formula form    full join form.parameter param

    With关键字-指定条件from Cat as cat left join cat.kittens as kitten    with kitten.bodyWeight > 10.0Fetch关键字-立即抓取from Cat as cat inner join fetch cat.mate    left join fetch cat.kittensfrom Document fetch all properties order by name隐式连接:from Cat as cat where cat.mate.name like '%s%'

    在使用上一定要区别立即加载与延迟加载,如果是立即加载:即from Customer c left outer join fetch c.orders Query query = session.createQuery("from Customer c left outer join fetch c.orders ");Iterator it = query.list().iterator();System.out.println(query.list().size());while(it.hasNext()) { Customer c = (Customer)it.next(); System.out.println(c.getCustomerId() + c.getOrders().size());}

    如果是延迟加载:Query query = session.createQuery(“from Customer c ” +       “left outer join c.orders ");Iterator it = query.list().iterator();System.out.println(query.list().size());while(it.hasNext()) { Object[] object = (Object[]) it.next(); Customer c = (Customer)object[0]; Order o = (Order) object[1]; System.out.println(c.getCustomerId() + o.getOrderId());}无论是内连接还是外连接,Hibernate都区分成立即或延迟。

    ==================================================================orders表中加上一个customer_id然后再加入一个customers表,里面有id和customerName字段。

    实体类Customer

    package vo ;

    public class Customer{ private int id ; private String customerName ; private Set orders = new HashSet() ; //getters and setters }

     

    <hibernate-mapping auto-import="true" package="vo">

     <class name="Customer" table="customers">  <id name="id">   <generator class="identity"/>  </id>  <property name="customerName" />  <set name="orders">   <key column="customer_id" />   <one-to-many class="Order" />  </set> </class>

     <class name="Order" table="orders">  <id name="id" column="order_id">   <generator class="identity"/>  </id>  <property name="orderDate" column="order_date"/>  <property name="shipAddress" column="ship_address"/>  <property name="currItemId" column="curr_item_id" />  //值集合,不需要将lineItems作为class单独映射了。  <list name="lineItems" table="line_items">   <key column="order_id" />   <list-index column="item_id" base="1" />   <composite-element class="LineItem">    <property name="productName" column="product_name"/>    <property name="quantity" column="quantity"/>   </composite-element>  </list>    

     </class> </hibernate-mapping>----------------------------------------------------Query q = session.createQuery("from Customer c inner join c.orders o");//Query q = session.createQuery("from Customer c inner join fetch c.orders o");//fetch的作用:将order全部加载进Customer里面,没有延迟//没有fetch的话,返回的是Object数组,有fetch的话,返回的是inner join之前的对象,把后面的//对象全部加载进前面的对象中去。

    Iterator it = q.list().iterator() ;

    //这个循环用来验证不加fetch的情况……while(it.hasNext()){ Object[] obj = (Object[])it.next() ; Customer c = (Customer)obj[0] ; Order o = (Order)obj[1] ;  System.out.println(c+":"+Hibernate.isInitialized(c.getOrders())); //如果有fetch的话,上面会打印true,没有的话那肯定是打印false! System.out.println(c.getCustomerName()+" : "+o.getId()+c.getOrders().size());}

    //下面这个循环用于有fetch的情况while(it.hasNext()){ Customer c = (Customer)it.next() ; System.out.println(c.getCustomerName()+" : "+c.getOrders().size()) ;}

    上面的HQL语句取出的集合类似下面的形式: C1       O1 C1       O2 C1       O3 C2       O1 C2       O2 C2       O3 C2       O4

    ========================================================================

    性能提升:

    如果插入10万个订单的话:

    for(int i = 0 ; i < 100000 ; i ++){ Order order = new Order() ; order.setOrderDate(new Timestamp(………………)) ; order.setShipAddress("kfasdj") ;

     session.save(order1);}

    但是由于session里面有缓存,每次save之后session都要缓存一个,这样下去可能会有缓存被沾满的异常。

    这种问题如何解决?

    由于对象被缓存在session中导致内存溢出,改进后:for ( int i=0; i<100000; i++ ) { Customer customer = new Customer(.....); session.save(customer); if ( i % 20 == 0 ) {  session.flush();  session.clear();  //隔一段时间就要将session给clear掉。 }}

    做为一种选择,也可以考虑使用StatelessSession,它的所有操作都是数据库级的。也就是说它的操作都将立即执行。但这并不代表在事务结束前就看到插入的结果。StatelessSession包括的方法有delete/update/insert,与SQL同名。

    它和Session本质上的区别就是是否对被操作的对象进行缓存。无状态Session不缓存。这就有个问题,如果同一个对象save两次的话,使用Session的话由于有副本可以比较就不会再次插入了;但是如果使用StatelessSession的话,动态更新什么的就无从谈起了。---------------------------------------------------------------------hibernate.jdbc.batch_size可以批处理数据库操作,Hibernate建议将其设置在5-30之间此外,batch_size可以做为以下元素的属性在映射文件中出现:1、类:class、subclass、joined-subclass、union-subclass2、集合:set、bag、idbag、list、map、array、primtive-array

    类--用于一对一、多对一的情况:<class name="Customer" table="customers" batch-size="5" >List ol = session.createQuery("from Order").list();Iterator it = ol.iterator();int i = 0;while(it.hasNext()) {  Order o = (Order)it.next(); System.out.println(o.getOwner().getCustomerName());}

    集合--用于一对多、多对多的情况<set name="orders" cascade="all" lazy="false" batch-size=“5”>      <key column="customer_id"/>      <one-to-many class="Order" /></set>与类级别的batch基本相同弥补了延迟加载情况下批量操作及立即加载产生的N+1问题。SQL语句如下:select * from customers where id in (?, ?, ?, ?, ?)----------------------------------------------------------

    Query q = session.createQuery("from Order");Iterator it = q.list().iterator() ;while(it.hasNext()){ Order or = (Order)it.next() ; System.out.println(or.getCustomer().getCostomerName()) ;}

    //要执行批量查询的话,需要下面的配置<class name="Customer" table="customers" batch-size="5">

    </class>

    这就是一次执行5个。batch-size在取关系的时候才有用。

    ========================================================================延迟加载:

    在对象与数据库关联时,其属性与关系是在使用时才加载,还是在对象关联数据库时就加载?Hibernate提供了两种最基本加载策略,即立即加载和延迟加载。总的来说,Hibernate中的延迟加载可以分为三级,即类级别的延迟加载、属性级别的延迟加载、关系级别的延迟加载。通常关系的延迟加载是最为常用的。

    主要的延迟加载配置包括:1、hibernate-mapping的default-lazy属性2、class、subclass、joined-subclass、union-subclass的lazy属性3、property的lazy属性4、many-to-one、one-to-one、key-many-to-one、many-to-many的lazy属性5、set、list、bag、map的lazy属性6、any、component的lazy属性

    为什么要延迟加载?如果一对多的关系数目很多的话,一次都加载进来会开销很大,所以延迟加载的目的是当你用到这些关系的集合的时候(一定要保证session没有关闭,否则必须使用lock将之重新关联,然后用initialize初始化)再加载进来。

    ------------------------------------------------------------------------------

    N+1查询问题:

    设A与B存在一对多关系,如果需要读取N个A,那么与A相关联的B如何加载? 策略一:将N个A先读取出来,再依据每个A的主键到B表中查找与之关联的B。共需要N+1一次查询。 策略二:使用IN操作符 策略三:使用左外连接left outer join

    策略一:

    先加载A:select * from a where ……再加载与每个A相对应的B(aref为指向A的外键):select * from b where aref=1select * from b where aref=2……………select * from b where aref=n-1select * from b where aref=n有多少个A就要执行多少次这样的语句,加上第一句加载A,一共N+1句

    策略二:

    先加载A:select * from a where ……再通过IN操作符加载B:select * from B where aref in(1, 2, ……, n-1, n)

    策略三:

    在加载A的同时加载B:select * from A a left outer join B b         on a.id=b.arefwhere …..但必然是在加载A时同时加载B,不能分开加载,因此无法实现延迟加载

    ----------------------------------------------------------

    抓取策略:

    Hibernate2中使用outer-join来定义抓取策略,它被赋予以下一些元素:1、集合:set、list、bag、map、array、primitive-array、idbag2、关系:many-to-one、one-to-one、many-to-manyouter-join取值为true、false或auto

    在Hibernate3中新增加了fetch属性,它与outer-join共存。显然,Gavin King是希望用fetch来取代outer-join,这一点可以从其官方文档中看出来。fetch的取值为select、join或subselect抓取策略对session.load、session.get起作用但对Query取得多个对象时不起作用。

    =====================================================================

    左外连接和批量的batch提高了系统的性能。

    抓取策略在以下情况中使用:1 使用Session.load()或者Session.get()的时候2 使用Criteria查询文档的第174页 19.1.2中列出了详细的使用背景……

    使用的时候将fetch属性设置为join就可以了。 


    最新回复(0)