首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Hibernate 超适用解析(二)

2012-07-15 
Hibernate 超实用解析(二)双向的一对多关系:要实现一个简单的从Parent到Child的<one-to-many>关联<many-to

Hibernate 超实用解析(二)
双向的一对多关系:
要实现一个简单的从Parent到Child的<one-to-many>关联
<many-to-one name="parent" column="parent_id" not-null="true"/>
(还需要为类Child添加parent属性)

现在实体Child在管理连接的状态,为了使collection不更新连接,使用inverse属性。

<set name="children" inverse="true">
    <key column="parent_id"/>
    <one-to-many inverse="true" cascade="all">
    <key column="parent_id"/>
    <one-to-many column="name" type="java.lang.String"/>
    <key-property name="password" column="password" type="java.lang.String"/>
</composite-id>

Hibernate 3中引入了动态模式,可以使用容器充当Java实体,在构造系统原型时灵活变化,而不必实际定义Java对象。
直接在映射文件的<class>标签上使用entity-name属性:
<class entity-name="EntityName" table="T_NAME">

entity-name属性设定的名称将在储存或载入时使用,例如可以如下储存:

Map entity = new HashMap();
entity.put("name", "zxz");

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx= session.beginTransaction();
session.save("EntityName", entity);
tx.commit();
session.close();
Map容器的key用来表示属性名称,而value用来表示值。
加载:
Map entity = (Map) session.load("EntityName", new Long(1));
System.out.println(entity.get("name"));
使用HQL查询:EntityName为配置文件中定义的entity_name属性值。
List entitys = session.createQuery("from EntityName").list();
      
for(int i = 0; i < entitys.size(); i++) {
    Map entity = (Map) entitys.get(i);
    System.out.println(entity.get("name"));
}

为了设计上的弹性,动态模型也可以与静态的POJO模型混用,要混用静态模型与动态模型,可在配置文件中设定name与entity-name属性:
<class name="ClassName" entity-name="EntityName" table="T_NAME">
默认是使用pojo来操作,要使用动态模型必须:
Map entity = new HashMap();
entity.put("name", "zxz");
Session dynamicSession = session.getSession(EntityMode.MAP);
Transaction tx= dynamicSession.beginTransaction();
dynamicSession.save("EntityName", entity);

继承关系:Hibernate自动判断继承关系。
可以采取三种策略,
  第一种:
(1)Table per concrete class,即抽象父类不建表,每一个子类建立一个表格。抽出相同属性部分定义为抽象父类,然后分别定义继承父类的子类,并分别定义配置文件。(HQL:from ParentClass 多表多次查询)。
分析:这种方式建议用于没有关联性,而且父类将来不会修改的情况,或者不需要多表查询的情况。
(2)也可在一个配置文件中,将父类与子类的属性对应撰写在一起。只建立一个配置文件(多表一次查询)。

<!-- abstract 表明ParentClass是抽象的,无需对应至任何表格 -->  
<class name="ParentClass" abstract="true">      
   <id name="id">
         <generator table="T_NAME1">
     <property name="someProperty"/> 
  </union-subclass>  

    <!-- 子类别的新增属性 -->
  <union-subclass name="subClass2" table="T_NAME2">
     <property name="otherProperty"/>
  </union-subclass>
</class>
分析:新增时要先查询出最大ID值再分别进行插入。利用子查询在同一个SQL语句中完成所有查询。Table per concrete class的继承映射方式是最简单,但没有效率。

   第二种:Table per subclass。父类与子类分别建表,而父类与子类对应的表通过外键来产生关联。只建立一个配置文件。
(多表一次查询,外键连接查询)
<class name="ParentClass" table="T_NAME">
<id name="id" column="id">
<generator foreign-key="id"/>
<property name="someProperty" column="someProperty" />
</joined-subclass>

<joined-subclass name="SubClass2" table="T_SUBNAME2">
            <key column="id" foreign-key="id"/>
            <property name="otherProperty" column="otherProperty" />       
</joined-subclass> 
</class>
###<joined-subclass>指明了子类所对应的表,<key column>指明子类对应表中,与父类的主键对应的外键一致。
分析:效率是这个映射类型需要考虑的,在复杂的类别继承下,新增资料必须对多个表格进行,而查询时,跨越多个表格的join也可能引发效率上的问题。如果需要多表查询,而子类相对来说有较多新增的属性,则可以使用这种映射方式。

第三种:
(1)Table per class hierarchy的继承映射方式,这种方式使用一个表储存同一个继承级别的所有类,并使用额外的属性来表示所记录的是哪一个子类的资料。
create table T_NAME (
    id bigint not null auto_increment,
    subType varchar(255) not null,
    name varchar(255),
    someProperty varchar(255),
    otherProperty varchar(255),
    primary key (id)
)
如果要储存的资料是来自SubClass1,则在subType记下"sub1",如果储存的资料来subClass2,则在subType记下"sub2",由subType就可以判断是要使用subClass1或subClass2,在映射文件中使用<discriminator>等相关标签来定义。
<class name="User" table="T_USER">
         <id name="id" column="id">
             <generator discriminator-value="sub1">
             <property name="someProperty" column="someProperty" />
         </subclass>
          <subclass name="SubClass2" discriminator-value="sub2">
             <property name="otherProperty" column="otherProperty"/>
         </subclass>
</class>
分析:因子类属性的不同,所以储存时会有许多字段没有值,但查询效率较好。

(2)也可以不使用专门定义一个字段来记录子类的类型,这适用于在使用一个已有数据库的情况,无法新增字段来记录子类类型。
配置文件只需要修改 <discriminator column="subType"/>为<discriminator formula="case when someProperty is not null then 'sub1' else 'sub2' end"/>即可。在<discriminator>上,设定foumula属性,根据回传值为sub1或sub2来判断是哪个子类。
分析:在需要多表查询,而子类属性相对比较少时,可以使用这种映射方式。这种方式会有大量的字段为NULL的情况,好处是使用一个表,查询时只需一次SQL。


Set:
(1)非实体(Entiy)时的映射方式,简单的说,也就是所包括的对象没有主键(Identity),只是纯綷的值类型(Value type)。
例:为了不允许重复的邮件位址记录,所以使用Set物件。在所包含的类中定义:
private Set emails;
public Set getEmails() {
        return emails;  
}
public void setEmails(Set emails) {
        this.emails = emails;
}
public void addEmail(String email) {
        this.emails.add(email);
}
public void removeEmail(String email) {
        this.emails.remove(email);  
}
要映射Set集合,可以使用另一个表来储存Set集合中的资料.
create table email (
    id bigint not null,
    address varchar(255)
)

create table user (
    id bigint not null auto_increment,
    name varchar(255),
    primary key (id)
)

alter table email add index id (id),
add constraint id  foreign key (id) references user (id)
<class name="User" table="user">
         <id name="id" column="id">
             <generator column="name"/>

          <set name="emails" table="email">
             <key column="id" foreign-key="id"/>
             <element type="string" column="address"/>
         </set>
</class>
User user1 = new User();
user1.setEmails(new HashSet());
user1.setName("Name");
user1.addEmail("name@gmail.com");
user1.addEmail("name@yahoo.com");
             
User user2 = new User();
user2.setEmails(new HashSet());
user2.setName("name1");
user2.addEmail("name1@gmail.com");

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();         
session.save(user1);
session.save(user2);
tx.commit();
session.close();

List是有序的结构:
private List items;
public List getItems() {
        return items;  
}  
public void setItems(List items) {
        this.items = items;
}  
public void addItem(String item) {
        items.add(item);  
}   
public void removeItem(String item) {
        items.remove(item);  
}

create table item (
    id bigint not null,
    name varchar(255),
    position integer not null,
    primary key (id, position)
)

create table user (
    id bigint not null auto_increment,
    name varchar(255),
    primary key (id)
)

alter table item
    add index id (id),
    add constraint id
    foreign key (id)
    references user (id)

<class name="User" table="user">
         <id name="id" column="id">
             <generator column="name"/>

          <list name="items" table="item">
             <key column="id"/>
          <list-index column="position"/>
             <element column="name" type="string"/>
          </list>
</class>
User user1 = new User();
user1.setItems(new ArrayList());
user1.setName("caterpillar");
user1.addItem("DC");
user1.addItem("CF Card");
      
User user2 = new User();
user2.setItems(new ArrayList());
user2.setName("momor");
user2.addItem("comics");

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();          
session.save(user1);
session.save(user2);
tx.commit();
session.close();


Map的特性是key/value对,容器中的每一个对象都有一个key与之对应,所以将Map集合的资料储存至数据库时,必须一同储存它的key值。
private Map items;
public Map getItems() {
        return items;  
}  
public void setItems(Map items) {
        this.items = items;  
}  
public void addItem(String name, String description) {
        items.put(name, description);  
}   
public void removeItem(String name) {
        items.remove(name);  
}
create table item (
    id bigint not null,
    description varchar(255),
    name varchar(255) not null,
    primary key (id, name)
)

create table user (
    id bigint not null auto_increment,
    name varchar(255),
    primary key (id)
)

alter table item
    add index id (id),
    add constraint id
    foreign key (id)
    references user (id)

<class name="onlyfun.caterpillar.User" table="user">
         <id name="id" column="id">
             <generator column="name"/>

          <map name="items" table="item">
             <key column="id" foreign-key="id"/>
             <map-key column="name" type="string"/>
             <element column="description" type="string"/>
         </map>    
</class>
User user1 = new User();
user1.setItems(new HashMap());
user1.setName("caterpillar");
user1.addItem("Book", "Java Gossip");
user1.addItem("DC", "Caxxx A80");
      
User user2 = new User();
user2.setItems(new HashMap());
user2.setName("momor");
user2.addItem("Doll", "Snoppy world");   

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.save(user2);
tx.commit();
session.close();

如果希望Set、Map等集合可以依一定的顺序来排列,可以从两个方面排序,一是在载入资料后于JVM中排序,另一是在数据库中直接使用order by子句来排序。要在JVM中进行排序,可以在映射文件中使用sort属性来定义集合的排序,这适用于Set与Map。Bag与List并不适用于这种方式,Bag或List本身是根据索引值来排列的。
<set name="emails" table="email" sort="natural">
sort="natural"表示使用对象的comparaTo()方法来进行排序,集合中的对象必须实现java.lang.Comparable接口。

另一种排序的方式则是在数据库中进行,直接使用order by子句来排序,这可以在映射文件中使用order-by属性来指定。
<set name="emails" table="email" order-by="address desc">
Hibernate在内部会使用LinkedHashMap或LinkedHashSet来作为集合,如果是Bag的话,则会在内部使用ArrayList作为集合。

双向关联(inverse 的意义)
在一对多、多对一形成双向关联的情况下,可以将关联维持的控制权交给多的一方,这样会比较有效率.所以在一对多、多对一形成双向关联的情况下,可以在“一”的一方设定控制权反转,也就是当储存“一”的一方时,将关联维持的控制权交给“多”的一方.<set name="users" table="user" cascade="save-update" inverse="true">

session.flush();强制存储对象.
session.evict(Object);将对象从Cache中删除.
session.clear();删除所有Cache中对象.
在SQL Server、Oracle等数据库中,可以在Hibernate配置文件中设定属性hibernate.jdbc.batch_size来控制每多少条记录就存储至数据库.
<session-factory>
        <property name="hibernate.jdbc.batch_size">100</property>
</session-factory>
在MySQL中暂不支持该功能。


Hibernate本身并未提供二级缓存的实现,而是由第三方(Third-party)产品来实现,Hibernate预设使用EHCache作为其二级缓存的实现,在最简单的情况下,只需在Hibernate下撰写一个ehcache.xml作为EHCache的资源定义文件,可以在 Hibernate下载档案中的etc目录下找到一个已经撰写好的ehcache.xml

sessionFactory.evict(User.class, user.getId());消除二级缓存.

如果打算在Hibernate中使用其它第三方产品进行缓存,则可以在hibernate.cfg.xml中定义 hibernate.cache.provider_class属性<property name="hibernate.cache.provider_class"> org.hibernate.cache.HashtableCacheProvider </property>
HashtableCache是Hibernate自身提供的二级缓存实现,不过性能与功能上有限,只用于开发时期的测试之用。

热点排行