zoukankan      html  css  js  c++  java
  • 三、ORM(Object/Relational Mapper)Hibernate对象映射

    一.                                                         ORM(Object/Relational Mapper)

    1.   OR映射---Hibernate基本数据类型

    Hibernate基本类型

    Java类型

    标准SQL字段类型

    boolean

    boolean, java.lang.Boolean

    BIT

    yes_no

    boolean, java.lang.Boolean

    CHAR(1) ( 'Y'/'N')

    true_false

    boolean, java.lang.Boolean

    CHAR(1) ( 'Y'/'N')

    byte

    byte, java.lang.Byte

    TINYINT

    short

    short, java.lang.Short

    SMALLINT

    integer

    int, java.lang.Integer

    INTEGER

    long

    long, java.lang.Long

    BIGINT

    float

    float, java.lang.Float

    FLOAT

    double

    double, java.lang.Double

    DOUBLE

    big_decimal

    java.math.BigDecimal

    NUMBERIC

    character

    java.lang.String

    CHAR(1)

    string

    java.lang.String

    VARCHAR

    date

    java.util.Date, java.sql.Date

    DATE

    time

    java.util.Date, java.sql.Time

    TIME

    timestamp

    java.util.Date, java.sql.TimeStamp

    TIMESTAMP

    calendar

    java.util.Calendar

    TIMESTAMP

    calendar_date

    java.util.Calendar

    Date

    clob

    java.sql.Clob

    CLOB

    blob

    java.sql.Blob

    BLOB

    binary

    byte[]

    VARBINARY, BLOB

    text

    java.lang.String

    CLOB

    seralizable

    java.io.Serializable

    VARBINARY, BLOB

    class

    java.lang.Class

    VARCHAR

    local

    java.util.Locale

    VARCHAR

    timezone

    java.util.TimeZone

    VARCHAR

    currency

    java.util.Currency

    VARCHAR

    2.   OR映射---实体映射示例

    *.hbm.xml, 主体内容包含表/类映射, id映射, 属性字段映射三个部分.示例文件:

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE hibernate-mapping PUBLIC

      "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

      "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

    <hibernate-mapping>

      <class name="cn.thinkmore.hibernate.pojo.Tuser" table="T_user">

         <id column="id" name="id"  type="java.lang.Integer">

             <generator class="increment" />

         </id>

         <property column="name" name="name" type="java.lang.String"/>

      </class>

    </hibernate-mapping>

    XML文件头定义了文件的编码方式, DTD与Hibernate的版本有关, 上面是使用hibernate 3.x使用的DTD.

    3.   OR映射---表/类映射

    <class name="cn.thinkmore.hibernate.pojo.Tuser" table="T_user">

    name: 指定了Java数据封装的POJO类类型.

    table: 指定了数据库的表名

    4.   OR映射---id映射

    <id name="id" type="java.lang.Integer" column="id">

         <generator class="increment" />

    </id>

    name: 指定了映射POJO类的属性名,

    type: POJO类中属性的数据类型;

    column: 数据库表的主键字段;

    generator子元素:由其class属性指定主键生成方式:

    • assigned: 主键由应用程序产生, 不需要hibernate干预
    • identity: 使用数据库的主键生成机制, 如MySQL, DB2、SQL Server的自增主键.
    • sequence: 使用数据库的sequence机制, 如Oracle的sequence
    • uuid.hex: 由Hibernate基于128位唯一值产生算法, 根据ip, 时间, jvm启动时间, 内部自增量生成十六进制的数值, 编码后成为一个32位长的字符串. 该方法提供了最好的数据库插入性能和数据库平台适应性.
    • uuid.string: 与uuid.hex类似, 只是生成的主键没有进行编码, 只有16位长度. 在某些数据库可能出错.
    • hilo: 通过hilo算法实现主键生成, 需要额外的数据库表保存主键生成历史状态.
    • seqhilo: 与hilo类似, 只是主键历史状态保存在sequence中, 适用于支持sequence的数据库.
    • increment: 主键按数值递增, 但如果多个实例同时访问同一个数据库, 各自生成主键,则容易造成主键重复.
    • native: 由hibernate根据数据库适配器中的定义, 自动采用identity, hilo, sequence中的一种方式.
    • foreign: 外部表的字段作主键.
    • select: hibernate 3 中新增的.需要提供一个唯一的标识字段进行二次读取, 以获取触发器生成的主键值, 通过param子元素进行定义, 比如:

    <generator class="select">

        <param name="key">key_field</param>

    </generator>

       该方法主要针对遗留系统的改造工程, 一些早期的系统主键依赖于触发器生成. 当数据库insert时, 触发器捕获这一操作, 并为主键赋值, 在插入数据库后, 再次读取某一识别字段读取已经插入的数据, 获取其主键值.

    5.   OR映射---复合主键映射(composite-id)---实体属性组成主键

    复合主键使用<composite-id>将取代id元素, 并具有property属性列表.

    <composite-id>

          <key-property column="userid" name="userid" type="java.lang.String" />

          <key-property column="when" name="when" type="java.sql.Date" />

        </composite-id>

    复合主键的POJO类需要实现equals和hashcode方法, 可以使用apache commons lang包中的工具类实现(commons-lang.jar), 比如:

     

    import org.apache.commons.lang.builder.EqualsBuilder;

    import org.apache.commons.lang.builder.HashCodeBuilder;

    import org.apache.commons.lang.builder.ToStringBuilder;

     

    ...

     

    public String toString(){

        return new ToStringBuilder(this)

            .append("userid ", getUserid ())

            .append("when", getWhen ())

            .toString();

      }

     

      public boolean equals(Object other){

        if(!(other instanceof MyPoJoClass)){

           return false;

        }

        MyPoJoClass castOther=( MyPoJoClass)other;

        return new EqualsBuilder()

            .appendSuper(super.equals(other))

            .append(this.getUserid (),castOther.getUserid() )

            .append(this.getWhen (),castOther.getWhen() )

            .isEquals();

      }

     

      public int hashCode(){

        return new HashCodeBuilder()

            .appendSuper(super.hashCode() )

            .append(getUserid ())

            .append(getWhen ())

            .toHashCode();

      }

    装载复合主键的记录时, 考虑把类对应的对象的主键值填充好后作为load的第二个参数.

    MyPoJoClass obj = new MyPoJoClass();

    obj.setUserid(...);

    obj.setWhen(...);

    obj=(MyPoJoClass)session.load(MyPoJoClass.class, obj);

    6.   OR映射---复合主键映射(composite-id)---基于主键类

    可以用一个独立的类来描述主键, 示例:

    <composite-id name="keyClassProperty" class="MyPoJoClass">

          <key-property column="userid" name="userid" type="java.lang.String" />

          <key-property column="when" name="when" type="java.sql.Date" />

        </composite-id>

    此时, 需要定义一个新的类KeyClass来作为主键类, KeyClass实现equals和hashcode方法, 而在POJO中, 使用属性名keyClassProperty来表示主键, 其类类型为KeyClass.

    7.   OR映射---composite-id其他子节点

    composite-id除了key-property子节点外, 还具有可选<key-many-to-one>子节点.完整的节点内容:

    <composite-id name="propertyName"

    class="ClassName"

    unsaved-value="any|none" >

     

          <key-property column="colum_name"

    name=" propertyName "

    type="type_name" />

    ...

          <key-many-to-one  column=" colum_name " calss=" ClassName "

      name=" propertyName " />

    ...

        </composite-id>

    8.   OR映射---属性/字段映射

    <property name="name" type="java.lang.String" column="name" />

    name: POJO类的属性名,

    type: POJO类中属性的数据类型; 如果是字符串,可以省略

    column: 数据库中的字段名. 如果和属性同名, 可以省略.

    9.   OR映射---自定义数据类型 UserType, CompositeUserType接口

    hibernate3 中的为: org.hibernate.usertype(hibernate2中为net.sf包)下面的两个接口: UserType 和 CompositeUserType . 它们提供自定义类型的规范, 这里以UserType为例.

    package org.hibernate.usertype;

     

    import java.io.Serializable;

    import java.sql.PreparedStatement;

    import java.sql.ResultSet;

    import java.sql.SQLException;

     

    import org.hibernate.HibernateException;

     

    public interface UserType {

     

         /**

    * 返回本类型所映射的SQL类型, 返回的是int[]数组. 可以取java.sql.Types中的int静态常量.

    * 比如有: public final static int DOUBLE  =   8;

          */

         public int[] sqlTypes();

     

         /**

          * 定义本类型的nullSafeGet() 方法返回的数据的类型

          */

         public Class returnedClass();

     

         /**

    * 定义两个数据的比较方法, 返回true表示相等, false表示不等.

          */

         public boolean equals(Object x, Object y) throws HibernateException;

     

         /**

          * Get a hashcode for the instance, consistent with persistence "equality"

          */

         public int hashCode(Object x) throws HibernateException;

     

         /**

          * 定义从ResultSet中读取数据并转换成自定义类型的方法, 对数据库null应该考虑

        * 参数names 包含了当前自定义类型的映射字段名称

          */

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner)  

    throws HibernateException, SQLException;

     

         /**

          * Hibernate进行数据保存时被调用的方法

          * @param st a JDBC prepared statement

          * @param value the object to write, 类型应该是returnedClass()方法返回的Class指定的类型

          * @param index statement parameter index

          * @throws HibernateException

          * @throws SQLException

          */

         public void nullSafeSet(PreparedStatement st, Object value, int index)

    throws HibernateException, SQLException;

         /**

          * 自定义的对象复制方法, 用作构造返回对象.

        * nullSafeGet方法被调用后, Hibernate获得了自定义数据对象,

    * 这个对象成为数据库读出的原始对象, 通过deepCopy方法, Hibernate

    * 返回一个复本给用户. 脏数据检查时, 如果两个对象不等(equals方法返回false),

    * 就会执行数据库持久化操作.

          * @param value the object to be cloned, which may be null

          * @return Object a copy

          */

         public Object deepCopy(Object value) throws HibernateException;

     

         /**

          * Are objects of this type mutable?

          * 本类型实例是否可变

          * @return boolean

          */

         public boolean isMutable();

     

         /**

    * 把对象转化为二级缓存中保存. 经常把当前对象对其他对象的引用, 保存为其id值.

          * Transform the object into its cacheable representation. At the very least this

          * method should perform a deep copy if the type is mutable. That may not be enough

          * for some implementations, however; for example, associations must be cached as

          * identifier values. (optional operation)

          *

          * @param value the object to be cached

          * @return a cachable representation of the object

          * @throws HibernateException

          */

         public Serializable disassemble(Object value) throws HibernateException;

     

         /**

        * 把二级缓存中获取的数据转换为自定义的对象数据

          * Reconstruct an object from the cacheable representation. At the very least this

          * method should perform a deep copy if the type is mutable. (optional operation)

          *

          * @param cached the object to be cached

          * @param owner the owner of the cached object

          * @return a reconstructed object from the cachable representation

          * @throws HibernateException

          */

         public Object assemble(Serializable cached, Object owner) throws HibernateException;

     

         /**

          * During merge, replace the existing (target) value in the entity we are merging to

          * with a new (original) value from the detached entity we are merging. For immutable

          * objects, or null values, it is safe to simply return the first parameter. For

          * mutable objects, it is safe to return a copy of the first parameter. For objects

          * with component values, it might make sense to recursively replace component values.

          *

          * @param original the value from the detached entity being merged

          * @param target the value in the managed entity

          * @return the value to be merged

          */

         public Object  replace(Object original, Object target, Object owner)

     throws HibernateException;

    }

     

    如果一个用户具有多个email, 可以分别定义多个字段保存, 也可以定义一个子表专门保存, 但都有点大动干戈. 可以考虑用一个字符串字段保存, 相邻的email用一个;隔开. 比如:

    package cn.thinkmore.hibernate.pojo;

     

    import java.io.Serializable;

    import java.sql.PreparedStatement;

    import java.sql.ResultSet;

    import java.sql.SQLException;

    import java.sql.Types;

    import java.util.*;

     

    import org.hibernate.Hibernate;

    import org.hibernate.HibernateException;

    import org.hibernate.usertype.UserType;

     

    public class EmailList implements UserType {

     

         private static final String SPLITTER = ";";

     

         private static final int[] TYPES = new int[] { Types.VARCHAR };

     

         public Object assemble(Serializable cached, Object owner)

                       throws HibernateException {

                return null;

         }

     

         public Object deepCopy(Object value) throws HibernateException {

                if (!(value instanceof List)) {

                       return null;

                }

                List src = (List) value;

                List tar = new ArrayList();

                tar.addAll(src);

                return tar;

         }

     

         public Serializable disassemble(Object value) throws HibernateException {

                return null;

         }

     

         public boolean equals(Object x, Object y) throws HibernateException {

                if (x == y)

                       return true;

                if (x != null && y != null) {

                       List xList = (List) x;

                       List yList = (List) y;

                       if (xList.size() != yList.size())

                              return false;

                       for (int i = 0; i < xList.size(); i++) {

                              String strX = xList.get(i).toString();

                              String strY = yList.get(i).toString();

                              if (!strX.equals(strY))

                                     return false;

                       }

                }

                return false;

         }

     

        

         public boolean isMutable() {

                return false;

         }

     

         public Object nullSafeGet(ResultSet rs, String[] names, Object owner)

                       throws HibernateException, SQLException {

                String value = Hibernate.STRING.nullSafeGet(rs, names[0]).toString();

                if(null!=value)

                       return parse(value);

                return null;

         }

     

         private List parse(String value) {

            String[] strs = org.apache.commons.lang.StringUtils.split(value,SPLITTER);

                List<String> emails = new ArrayList<String>();

                for(int i=0; i<strs.length;i++){

                       emails.add(strs[i]);

                }

            return emails;

         }

        

         private String assemble(List list) {

                StringBuilder sb = new StringBuilder();

                int maxIndex = list.size()-1;

                for(int i=0; i<maxIndex;i++){

                       sb.append(list.get(i)).append(SPLITTER);

                }

                if(0<=maxIndex){

                       sb.append(list.get(maxIndex));

                }

                return sb.toString();

         }

        

         public void nullSafeSet(PreparedStatement st, Object value, int index)

                       throws HibernateException, SQLException {

             if(null!=value&&(value instanceof List)){

                    String str = assemble((List)value);

                    Hibernate.STRING.nullSafeSet(st,str,index);

             }else if(null!=value){

                    Hibernate.STRING.nullSafeSet(st,value.toString(), index);

             }else{

                    Hibernate.STRING.nullSafeSet(st,null, index);

             }

         }

     

         public Object replace(Object original, Object target, Object owner)

                       throws HibernateException {

                return null;

         }

     

         public Class returnedClass() {

                return List.class;

         }

     

         public int[] sqlTypes() {

                return TYPES;

         }

     

         public int hashCode(Object x) throws HibernateException {

                return 0;

         }

    }

    数据关联关系

    数据关联关系在实体的子类里面不会被自动继承.

    10. 一对一关联 ----- 主键关联 one-to-one

    一对一的主键关联, 指两个表的记录是一对一的关系, 且一个表的主键取为另一个表对应的记录的主键.

    <hibernate-mapping>

        <class name="cn.thinkmore.hibernate.pojo.Citizen" table="citizen" lazy="true">

           <id name="id" column="id">     

               <generator class="uuid.hex" />

           </id>

           <property name="name" />

           <property name="sex"/>

           <property name="idcard" />

           <property name="address"/>

            <one-to-one name="gene"  cascade="all"  outer-join="true"

                       class="cn.thinkmore.hibernate.pojo.Gene" />

        </class>

    </hibernate-mapping>

     

    name: 属性名

    class: java全路径类名

    cascade: 是否级连操作, all , none

    outer-join: 是否外链接, true/false

    <hibernate-mapping>

        <class name="cn.thinkmore.hibernate.pojo.Gene" table="gene" lazy="true">

           <id name="id" column="id" >    

               <generator class="foreign">

                 <param name="property">citizen</param>

               </generator>

           </id>

           <property name="dna" />

           <property name="chromosome"/>

           <property name="species"/>

           <one-to-one name="citizen"  constrained="true"

                       class="cn.thinkmore.hibernate.pojo.Citizen" />

        </class>

    </hibernate-mapping>

     

    Constrained: 次关联中的约束方.

    .....

    Session s = HibernateSession3.getSession();     

           Citizen one = new Citizen();

           one.setIdcard("123456789012345678");

           one.setName("Mike");

           one.setSex("M");

           Gene gene = new Gene();

           gene.setBloodType("AB");

           gene.setDna("Ajodjjd[[joa");

           gene.setSpecial("White");

           one.setGene(gene);

           gene.setCitizen(one);

           Transaction trans = s.beginTransaction();

           s.saveOrUpdate(one);

           trans.commit();

           // 两条数据一起被保存, 查询时也会关联查出来.

           Query query = s.createQuery("From  Citizen");

           List list = query.list();

           for (int i = 0; i < list.size(); i++) {

               one = (Citizen) list.get(i);

               if (null == one)

                  continue;

               gene = (Gene) one.getGene();

               if (null != gene){

                System.out.println(gene.getId()+":"+gene.getSpecial()+":"+gene.getDna());

               }

    }

    11. 单向一对多关联 ----- 唯一外键关联 one-to-many

     

    一个用户可以多个email:

     

    <set name="email" table="emails" cascade="all">

        <key column="userid" />

    <one-to-many calss="....Email" />

    </set>

     

    12. 双向一对多关联

     

    除了在主控方配置一对多, 还要在被控方配置对应的多对一关系:

     

    User:

    <set name="email" table="emails" cascade="all" inverse="true">

        <key column="userid" />

    <one-to-many calss="....Email" />

    </set>

     

    Email:

    <many-to-one  name="user" class=".....User" cascade="none" column="userid" />

     

    再有例子:一个职员(是人的子类)前后可能有多条工作(每次工作是一条记录):

    <hibernate-mapping >

        <class name="webapp.hibernate.pojo.People" table="people" lazy="false">

           <comment>Users may bid for or sell auction items.</comment>

           <id name="id">    

               <generator class="uuid.hex"/>

           </id> 

           <property name="name" />

           <property name="idcard" /> 

           <property name="sex"/>            

           <property name="birth" type="date"/>

          

            <one-to-one name="gene"

                        cascade="all"

                         class="webapp.hibernate.pojo.Gene"/>             

        </class>  

    </hibernate-mapping>

     

    <hibernate-mapping >

        <class name="webapp.hibernate.pojo.Employee" table="people" lazy="false">

           <id name="id">    

               <generator class="uuid.hex"/>

           </id> 

           <property name="name" />

           <property name="idcard" /> 

           <property name="sex"/>            

           <property name="birth" type="date"/>

           <one-to-one name="gene"

                        cascade="all"

                         class="webapp.hibernate.pojo.Gene"/>   

                        

           <set name="jobs" table="joblist" cascade="all" inverse="true">

                <key column="empid" />

                <one-to-many class="webapp.hibernate.pojo.JobList" />  

           </set>                 

        </class>  

    </hibernate-mapping>

     

    <hibernate-mapping >

        <class name="webapp.hibernate.pojo.JobList" table="joblist" lazy="false">

           <id name="id">    

               <generator class="uuid.hex"/>

           </id> 

    <!--       <property name="userid" column="empid"/> --> 

           <property name="company" />

           <property name="begin" type="date"/>            

           <property name="end" type="date"/>

           <many-to-one  name="employee" class="webapp.hibernate.pojo.Employee" cascade="none" column="empid" />       

        </class>  

    </hibernate-mapping>

    public static void testOne2Many() {

           Session s = HibernateSession3.getSession();

           Employee e = new Employee();

           e.setName("一对多");

           e.setIdcard("123456789012345678");

           e.setBirth(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*25));

          

           JobList job1 = new JobList();

           job1.setEmployee(e);

           job1.setCompany("A gongsi");

           job1.setBegin(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*5));

           job1.setEnd(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*4));

          

           JobList job2 = new JobList();

           job2.setEmployee(e);

           job2.setCompany("B gongsi");

           job2.setBegin(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*4));

           job2.setEnd(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*2));

          

           Set<JobList> jobs = new HashSet<JobList>();

           jobs.add(job1);

           jobs.add(job2);

           e.setJobs(jobs);

          

           Transaction trans = s.beginTransaction();

           // 两个表的数据一起被保存, 查询时也会关联查出来.

           s.saveOrUpdate(e);

           trans.commit();

           Query query = s.createQuery("From Employee");

           List list = query.list();

           for (int i = 0; i < list.size(); i++) {

               Employee one = (Employee) list.get(i);

               if (null == one)

                  continue;

               System.out.println(one.getName()+":"+one.getBirth()+":");

               Set joblist = one.getJobs();

               for(Object o:joblist){         

                  JobList j = (JobList)o;

                  System.out.println("\t工作:"+j.getCompany()+":"+j.getBegin()+":"+j.getEnd());

               }

               System.out.println("---------------------------------------");

           }

        }

     

    13. 双向多对多关联

    一个用户可以在银行有多个帐号

    User:

        <class name="webapp.hibernate.pojo.MoneyUser" table="people" lazy="false">

           <id name="id">     

               <generator class="uuid.hex"/>

           </id> 

           <property name="name" />

           <property name="idcard" /> 

           <property name="sex"/>            

           <property name="birth" type="date"/>

          

            <one-to-one name="gene"

                        cascade="all"

                         class="webapp.hibernate.pojo.Gene"/>

             <set name="banks" table="bankuser" cascade="none" >

                <key column="userid" />

                <many-to-many class="webapp.hibernate.pojo.Bank" column="bankid" /> 

             </set>

        </class>  

     

    Bank:

    <class name="webapp.hibernate.pojo.Bank" table="bank" lazy="false">

           <comment>Users may bid for or sell auction items.</comment>

           <id name="id">    

               <generator class="uuid.hex"/>

           </id> 

           <property name="bankname" />   

           <property name="type"/>           

           <property name="createday" type="date"/>

            <set name="cutomers" table="bankuser" cascade="none" >

                <key column="bankid" />

                <many-to-many class="webapp.hibernate.pojo.MoneyUser" column="userid" /> 

             </set>

        </class>  

        public static void testMany2Many() {

           Session s = HibernateSession3.getSession();

           Iterator<MoneyUser> itor =(Iterator<MoneyUser>)s.createQuery("FROM MoneyUser").iterate();

           while(itor.hasNext()){

               MoneyUser moneyUser =  itor.next();

               Set banks = moneyUser.getBanks();

               Iterator<Bank> bankItor = banks.iterator();

               while(bankItor.hasNext()){

                  Bank bank = bankItor.next();

                  System.out.println(moneyUser.getName()+"  "+bank.getBankname());

               }

           }

        }

       

        public static void testMany2Many2() {

           Session s = HibernateSession3.getSession();

           Iterator<Bank> itor = s.createQuery("FROM Bank").iterate();

           while(itor.hasNext()){

               Bank bank =  itor.next();

               Set customers = bank.getCutomers();

               Iterator<MoneyUser> customerItor = customers.iterator();

               while(customerItor.hasNext()){

                  MoneyUser moneyUser = customerItor.next();

                  System.out.println(bank.getBankname()+"  "+moneyUser.getName());

               }

           }

        }

     

  • 相关阅读:
    C# 委托应用总结
    C语言指针总结
    SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较
    C#接口
    C# Linq
    C#反射
    重写与重载
    mysql01
    ajax
    bootstrap02导航菜单
  • 原文地址:https://www.cnblogs.com/linzheng/p/1924852.html
Copyright © 2011-2022 走看看