Java框架之Hibernate(二)
本文主要介绍:
1 Criteria 接口
2 用 myeclipse 反向生成
3 hibernate 主键生成策略
4 多对一
5 一对多
6 使用List 集合的一对多
7 多对多
一、Criteria 接口
//例一 public static AdminInfo getLoginAdminCriteria(String adminName,String password){ try{ Session s=HibUtil.getSession(); Criteria c=s.createCriteria(AdminInfo.class); c.add(Restrictions.eq("adminName", adminName)); c.add(Restrictions.eq("password", password)); return (AdminInfo) c.uniqueResult(); }finally{ HibUtil.closeSession(); } }
QBC常用限定方法
Restrictions.eq --> equal,等于.
Restrictions.allEq --> 参数为Map对象,使用key/value进行多个等于的比对,相当于多个Restrictions.eq的效果
Restrictions.gt --> great-than > 大于
Restrictions.ge --> great-equal >= 大于等于
Restrictions.lt --> less-than, < 小于
Restrictions.le --> less-equal <= 小于等于
Restrictions.between --> 对应SQL的between子句
Restrictions.like --> 对应SQL的LIKE子句
Restrictions.in --> 对应SQL的in子句
Restrictions.and --> and 关系
Restrictions.or --> or 关系
Restrictions.isNull --> 判断属性是否为空,为空则返回true
Restrictions.isNotNull --> 与isNull相反
Restrictions.sqlRestriction --> SQL限定的查询
Order.asc --> 根据传入的字段进行升序排序
Order.desc --> 根据传入的字段进行降序排序
MatchMode.EXACT --> 字符串精确匹配.相当于"like 'value'"
MatchMode.ANYWHERE --> 字符串在中间匹配.相当于"like '%value%'"
MatchMode.START --> 字符串在最前面的位置.相当于"like 'value%'"
MatchMode.END --> 字符串在最后面的位置.相当于"like '%value'"
//例子 Criteria c=s.createCriteria(AdminInfo.class); // 查询 id在 10 到 30 之间的用户 c.add(Restrictions.between("id",10,30)).list(); //查询 adminName 在 张三,李四,王五之间的 String [] names={"张三","李四","王五"}; //这里也可以用集合 只要是从Collection派生的都可以 c.add(Restrictions.in("userName",names)).list(); //查询 note为空的用户 c.add(Restrictions.isNull("note")).list(); //查询年龄为空,或名字为admin的 c.add(Restrictions.or(Restrictions.eq("adminName", "admin"),Restrictions.isNull("age")));
//使用 QBC实现动态查询(以前的例子) public List<AdminInfo> getAdminList(String adminName,String password,String note) { String sql="select * from adminInfo where 1=1 "; if(adminName!=null){ sql+=" and adminName='"+addminName+"'"; } if(password!=null){ sql+=" and adminName='"+password+"'"; } if(note!=null){ sql+=" and adminName='"+note+"'"; } }
//上例使用 QBC public static List<AdminInfo> getLoginAdminList(String adminName,String password,String note){ try { Session s=HibUtil.getSession(); Criteria c=s.createCriteria(AdminInfo.class); if(adminName!=null){ c.add(Restrictions.eq("adminName", adminName)); } if(password!=null){ c.add(Restrictions.eq("password", password)); } if(note!=null){ c.add(Restrictions.eq("note", note)); } return c.list(); }finally{ HibUtil.closeSession(); } } //官方推荐使用HQL
二、用 myeclipse 反向生成
Hibernate 的开发流程
1) 由domain对象开始 bean -> 生成xml -> 生成数据库表
2) 由数据库表开始, 由表生成 bean 和 配置文件
3) 由配置文件开始
关于 <property name="hbm2ddl.auto">update</property> 这个配置
#Hibernate.hbm2ddl.auto create-drop //创建,用完之后再删除
#Hibernate.hbm2ddl.auto create //这个操作会重新创建表
#Hibernate.hbm2ddl.auto update //会把对象模型的变化,表现在关系模型上
#Hibernate.hbm2ddl.auto validate //当对象模型和关系模不一样的时候,会发生错误(通常生产环境下用)
三、Hibernate 主键生成策略
所有的主键生成器都实现了 org.Hibernate.id.IdentifierGenerator 接口
常用的主键生成器
1) increament: 适合所有的数据库, 由hiberante来维护主键的生成(先查询最大的,再加1), 有线程安全问题,不适合多个进程的情况。用于 long ,short ,int ,生成的是数字型的自增的唯一标识,在集群的情况下不要用实测,在mysql下,无论原来是否设置了自增主键,用它都可以
2) identity: 适合于mysql , mssqlserver 等支持自增主键的数据库,主键不由 Hibernate 来维护,类型是 int, short,long。oracle中不能用 (无法将 NULL 插入 ("SA"."USERINFO"."ID") 这样的异常)
3) sequence: (顺列) 适合于oracle等支持序列的数据库, 主键值不由Hibernate维护。mysql不支持(出现 Dialect does not support sequences 这样的异常), 在 oracle中直接这样写: <generator class="sequence" /> 它会对所有的表使用同一个序列。
4) native: 和底层数据库的具体特性有关, 如果是mysql或 mssqlserver ,它就会选择 identity, 如果是oracle 它会选 sequence
5) hilo: 高低位 根据 hilo算法 生成主键,支持所有的数据库, 需要一张额外的表生成的表名 默认是 Hibernate_unique_key 里面就一列 next_hi
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="hilo" >
<param name="table">t_key</param>
<param name="column">f_nextval</param>
<param name="max_lo">100</param>
</generator>
</id>
6) uuid : Hibernate 来维护的主键 ,支持所有数据库
<id name="id" > <column name="id" /> <generator class="uuid" /> </id> //附 用java程序生成uuid => UUID.randomUUID().toString() c6204dc1-b9b6-40a9-a03a-a35ab6e6adc
7) assigned : 主键由用户来维护, 如果选择了这种生成方式,则Hibernate不再帮我们生成主键,主键要由用户。在程序中指定,不指定将出错 ids for this class must be manually assigned before calling save():
四、多对一
//对象模型 //尼姑 public class NiGu { private int id; private String fahao; private NiGuAn niguan; // 用的是NiGuAn 对象 这样就建立起了一对多的关联 } //尼姑庵 public class NiGuAn { private int id; private String name; }
//映射文件 (NiGuAn.hbm.xml) <Hibernate-mapping> <class name="cat.beans.NiGuAn" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="name" /> </class> </Hibernate-mapping> NiGu.hbm.xml <Hibernate-mapping> <class name="cat.beans.NiGu" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="fahao" /> <many-to-one name="niguan" column="niguan_id" /> //让它生成的外键这列的名字叫 niguan_id </class> </Hibernate-mapping>
//添加的方法 public static void add(){ try{ NiGuAn niguan= new NiGuAn(); niguan.setName("普渡寺"); NiGu nigu=new NiGu(); nigu.setFahao("灭绝师太"); //建立起两对象模型的关联 nigu.setNiguan(niguan); Session s=HibUtil.getSession(); Transaction tx=s.beginTransaction(); s.save(niguan); //先存的是尼姑庵 s.save(nigu); tx.commit(); } finally{ HibUtil.closeSession(); } }
总结::
1) 只要对象模型建好了,生成关系模型(数据库表)的时候,也能正确的生成
2) 如果把上面的
s.save(niguan); 反过来写,会多生成一条sql语句,用来维护关联关系
s.save(nigu);
3) 如果不 s.save(niguan); 将出现下面的异常信息
object references an unsaved transient instance - save the transient instance before flushing: cat.beans.NiGuAn
对象引用了一个没有保存的瞬时对象
//查询 public static void main(String[] args) { NiGu nigu=searchNiGu(); System.out.println(nigu.getFahao()); //可以输出结果 /* 被关联的对象,查询的时候默认是使用懒加载的 如果下面的代码中没有 Hibernate.initialize(nigu.getNiguan()); 这句,将引发 could not initialize proxy - no Session */ System.out.println(nigu.getNiguan().getName()); } //关于查询的例二 public static NiGu searchNiGu(){ try{ Session s=HibUtil.getSession(); NiGu nigu= (NiGu) s.get(NiGu.class,1 ); Hibernate.initialize(nigu.getNiguan()); //通知Hibernate 直接把关联的数据查询出来 return nigu; }finally{ HibUtil.closeSession(); } } //关于查询的例一 public static void search(){ try{ Session s=HibUtil.getSession(); NiGu nigu= (NiGu) s.get(NiGu.class,1 ); System.out.println(nigu.getFahao()); System.out.println(nigu.getNiguan().getName()); //能否把关联的数据查出来 ? }finally{ HibUtil.closeSession(); } }
五、一对多
//对象模型 //尼姑庵 public class NiGuAn { private int id; private String name; private Set<NiGu>niguList; //一对多关联 }
//映射文件(NiGuAn.hbm.xml) <Hibernate-mapping> <class name="cat.beans.NiGuAn" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="name" /> <set name="niguList"> <key column="niguan_id" /> <one-to-many class="NiGu" /> </set> </class> </Hibernate-mapping>
//映射文件 (NiGuAn.hbm.xml) <Hibernate-mapping> <class name="cat.beans.NiGuAn" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="name" /> </class> </Hibernate-mapping> NiGu.hbm.xml <Hibernate-mapping> <class name="cat.beans.NiGu" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="fahao" /> <many-to-one name="niguan" column="niguan_id" /> //让它生成的外键这列的名字叫 niguan_id </class> </Hibernate-mapping>
//添加的方法 public static void add(){ NiGuAn niguan=new NiGuAn(); niguan.setName("少林寺"); NiGu nigu1=new NiGu(); nigu1.setNiguan(niguan); nigu1.setFahao("空闻"); NiGu nigu2=new NiGu(); nigu2.setNiguan(niguan); nigu2.setFahao("玄苦"); try{ Session s=HibUtil.getSession(); Transaction tx=s.beginTransaction(); /* Set<NiGu> niguList=new HashSet<NiGu>(); niguList.add(nigu1); niguList.add(nigu2); niguan.setNiguList(niguList); */ s.save(niguan); s.save(nigu1); s.save(nigu2); tx.commit(); }finally{ HibUtil.closeSession(); } public static void main(String[] args) { // add(); NiGuAn niguan= search2(); System.out.println(niguan.getName()); for(NiGu n:niguan.getNiguList()){ System.out.println(n.getFahao()); } } public static NiGuAn search2(){ try{ Session s=HibUtil.getSession(); NiGuAn niguan= (NiGuAn) s.get(NiGuAn.class, 4); Hibernate.initialize(niguan.getNiguList()); return niguan; }finally{ HibUtil.closeSession(); } } public static void search(){ try{ Session s=HibUtil.getSession(); NiGuAn niguan =(NiGuAn) s.get(NiGuAn.class, 4); System.out.println(niguan.getName()); Set<NiGu> niguList= niguan.getNiguList(); for (NiGu n:niguList) { System.out.println(n.getFahao()); } }finally{ HibUtil.closeSession(); } }
六、使用List 集合的一对多
对象模型中的set 要换成list
<!-- 把它换成下面的配置即可 <set name="niguList"> <key column="niguan_id" /> <one-to-many class="NiGu" /> </set> --> <list name="niguList"> <key column="niguan_id" /> <index column="indexNo" /> <one-to-many class="NiGu" /> </list>
//如果对象模型还是使用list,但关系模型中不要排序列,可以用bag 来配置 <bag name="niguList"> <key column="niguan_id" /> <one-to-many class="NiGu" /> </bag>
//添加方法 public static void add(){ NiGuAn niguan=new NiGuAn(); niguan.setName("九峰寺"); NiGu nigu1=new NiGu(); nigu1.setNiguan(niguan); nigu1.setFahao("空空大师"); NiGu nigu2=new NiGu(); nigu2.setNiguan(niguan); nigu2.setFahao("玄玄"); NiGu nigu3=new NiGu(); nigu3.setNiguan(niguan); nigu3.setFahao("灭绝"); try{ Session s=HibUtil.getSession(); Transaction tx=s.beginTransaction(); List<NiGu> niguList=new ArrayList<NiGu>(); niguList.add(nigu1); niguList.add(nigu2); niguList.add(nigu3); niguan.setNiguList(niguList); s.save(niguan); s.save(nigu1); s.save(nigu2); s.save(nigu3); tx.commit(); //说明 /* List<NiGu> nlist=niguan.getNiguList(); 没有问题 ArrayList<NiGu> nlist=(ArrayList<NiGu> )niguan.getNiguList(); 将出现异常 ClassCastException: org.Hibernate.collection.PersistentBag cannot be cast to java.util.ArrayList */ }finally{ HibUtil.closeSession()
七、多对多
//对象模型 public class Student { private int id; private String stuName; private Set<Teacher> teacherList; } public class Teacher { private int id; private String teacherName; private Set<Student> stuList; //建立关联 }
//Teacher.hbm.xml <Hibernate-mapping package="cat.beans"> <class name="Teacher" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="teacherName" /> <set name="stuList" table= "teacher_student" lazy="false"> //指明不要懒加载 <key column="teacher_id" /> <!-- 将来根据 teacher_id列表中间表找学生 --> <many-to-many class="Student" column="stucent_id" /> </set> </class> </Hibernate-mapping>
//Student.hbm.xml <Hibernate-mapping package="cat.beans"> <class name="Student" > <id name="id"> <column name="id" /> <generator class="native" /> </id> <property name="stuName" /> <set name="teacherList" table= "teacher_student"> <key column="student_id" /> <many-to-many class="Teacher" column="teacher_id" /> </set> </class> </Hibernate-mapping>
//测试 public static void main(String[] args) { Teacher t=search(); System.out.println(t.getTeacherName()); for(Student stu:t.getStuList()){ System.out.println(stu.getStuName()); } } public static Teacher search(){ return (Teacher) HibUtil.get(Teacher.class, 1); } public static void add(){ //老师的数据 Teacher t1=new Teacher(); t1.setTeacherName("成秀秀"); Teacher t2=new Teacher(); t2.setTeacherName("史老师"); Set<Teacher> teacherList=new HashSet<Teacher>(); teacherList.add(t1); teacherList.add(t2); //学生的数据 Student stu1=new Student(); stu1.setStuName("刘冰"); Student stu2=new Student(); stu2.setStuName("侯晨阳"); Set<Student> studentList=new HashSet<Student>(); studentList.add(stu1); studentList.add(stu2); t1.setStuList(studentList); //建立起老师和学生的关联 t2.setStuList(studentList); ///////有没有必要////// //stu1.setTeacherList(teacherList); //stu2.setTeacherList(teacherList); ////////////////////// Session s=HibUtil.getSession(); Transaction tx=s.beginTransaction(); s.save(t1); s.save(t2); s.save(stu1); s.save(stu2); tx.commit(); s.close(); }
高低位: 附:它的算法
next_hi * (max_lo + 1) + lo
1. 读取并记录数据库的Hibernate_unique_key表中next_hi字段的值,数据库中此字段值加1 并保存
2. nHibernate取得lo值(从0循环到(max_lo-1),到max_lo的时候,再执行步骤1,然后 lo继续从 0循环到(max_lo-1))
3. 最后根据公式计算主键值。
注意:
所谓的hilo性能比identity好的前提是在同一Session里批量添加记录的时候(id不用每添加一条记录都从数据库里返回);
如果两条记录在不同的Session添加,那么每条记录的id值会相隔32767的差值(max_lo默认值是 32767时)。
由于我们多数都是一条一条记录的添加的,这样如果按 max_lo的默认值32767来生成主键的话,其值很快就会暴增,甚至会出现溢出的可能。