
一. 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 |
*.hbm.xml, 主体内容包含表/类映射, id映射, 属性字段映射三个部分.示例文件:
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
XML文件头定义了文件的编码方式, DTD与Hibernate的版本有关, 上面是使用hibernate 3.x使用的DTD.
3. OR映射---表/类映射
name: 指定了Java数据封装的POJO类类型. table: 指定了数据库的表名 4. OR映射---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子元素进行定义, 比如: key_field 该方法主要针对遗留系统的改造工程, 一些早期的系统主键依赖于触发器生成. 当数据库insert时, 触发器捕获这一操作, 并为主键赋值, 在插入数据库后, 再次读取某一识别字段读取已经插入的数据, 获取其主键值. 5. OR映射---复合主键映射(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)---基于主键类 可以用一个的类来描述主键, 示例: 此时, 需要定义一个新的类KeyClass来作为主键类, KeyClass实现equals和hashcode方法, 而在POJO中, 使用属性名keyClassProperty来表示主键, 其类类型为KeyClass. 7. OR映射---composite-id其他子节点 composite-id除了key-property子节点外, 还具有可选 unsaved-value="any|none" > type="type_name" /> ... ... 8. OR映射---属性/字段映射 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 for(int i=0; i } return emails; } private String assemble(List list) { StringBuilder sb = new StringBuilder(); int maxIndex = list.size()-1; for(int i=0; i } 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 一对一的主键关联, 指两个表的记录是一对一的关系, 且一个表的主键取为另一个表对应的记录的主键. name: 属性名 class: java全路径类名 cascade: 是否级连操作, all , none outer-join: 是否外链接, true/false citizen Constrained: 次关联中的约束方. ..... Session s = HibernateSession3.getSession(); Citizen one = new Citizen(); one.setIdcard("1234567012345678"); 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: 12. 双向一对多关联 除了在主控方配置一对多, 还要在被控方配置对应的多对一关系: User: Email: 再有例子:一个职员(是人的子类)前后可能有多条工作(每次工作是一条记录): class="webapp.hibernate.pojo.Gene"/> class="webapp.hibernate.pojo.Gene"/> public static void testOne2Many() { Session s = HibernateSession3.getSession(); Employee e = new Employee(); e.setName("一对多"); e.setIdcard("1234567012345678"); 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 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("\工作:"+j.getCompany()+":"+j.getBegin()+":"+j.getEnd()); } System.out.println("---------------------------------------"); } } 13. 双向多对多关联 一个用户可以在银行有多个帐号 User: class="webapp.hibernate.pojo.Gene"/> Bank: public static void testMany2Many() { Session s = HibernateSession3.getSession(); Iterator while(itor.hasNext()){ MoneyUser moneyUser = itor.next(); Set banks = moneyUser.getBanks(); Iterator while(bankItor.hasNext()){ Bank bank = bankItor.next(); System.out.println(moneyUser.getName()+" "+bank.getBankname()); } } } public static void testMany2Many2() { Session s = HibernateSession3.getSession(); Iterator while(itor.hasNext()){ Bank bank = itor.next(); Set customers = bank.getCutomers(); Iterator while(customerItor.hasNext()){ MoneyUser moneyUser = customerItor.next(); System.out.println(bank.getBankname()+" "+moneyUser.getName()); } } }
