1.POJO类之数据类型注意事项
当我们创建了一个POJO类Dept部门。有主键字段deptNo int,cid int,name String ,loc String..导入hibernate相关包,创建hibernate.cfg.xml.创建Dept.hbm.xml。这些都设定完毕,用List<Dept> list = session.find("from Dept");用HQL来查询Dept表的所有字段,然后用增加的for循环遍历查出来的ist。for(Dept d:list)System.out.println(d.getdeptNo+":"+d.getName+":"+d.getLoc);执行时却报了一个"Null value was assigned to a property of primitive type setter of com.ts.pojo.Dept.cid".经查发现数据库中的cid字段有null值的。于是尝试抛开cid字段,用session.find("select new Dept(deptNo,dname,loc) from Dept d");注意要创建带这个三个字段的参数的构造方法。这样就不会报上面的错误。或者把POJO类里的基本数据类型int,boolean,float这些换成Integer,Boolean,Float就不会出现上面的错误而会把NULL值给显示出来。
2.hibernate之HQL语言
查询表College,通过HQL
try {
Session session = HibernateFactory.getHibernateSession();
Query query = session.createQuery("from College c");//如果要加条件可以+c.cid=2
List<College> list = query.list();
for(College c:list)//增加的for循环格式:元素类型 空格 元素变量 冒号 要循环的集合
System.out.println(c);//c要实现toString方法,否则打印出内存地址
} catch (HibernateException e) {
// TODO Auto-generated catch block()
e.printStackTrace();
}
通过标准SQL语句查询
try {
Session session = HibernateFactory.getHibernateSession();
Query query = session.createSQLQuery("select * from t_college");//如果要加条件可以+cid=2
List<Object[]> list = query.list();
for(Object[] c:list){
StringBuffer sb = new StringBuffer();
for(Object o:c)
sb.append(o+" ");
System.out.println(sb);
}
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
通过对比HQL和SQL。可以得出
1.两者都可以通过接口org.hibernate.Query来接收查询结果。
2.org.hibernate.Session既可以通过createQuery(HQL)来接收HQL查询语法,也可以通过createSQLQuery(SQL)来执行标准SQL查询语法。
3.查询出的结果,当使用HQL时直接返回具体的java类,像上面的College。方便我们使用类的方法进行操作。而使用SQL时只能用Object数组来接收每条纪录。
通过HQL更新表College。
Session session = HibernateFactory.getHibernateSession();
Transaction ts = session.beginTransaction();//纳入事务管理
try {
Query query = session.createQuery("update College c set c.cname='北大' where c.cid=2");//注意字符串里的字符要加引号的,且是单引号的。
//Query query = session.createQuery("update College c set c.cname=:cname where c.cid=:cid");//注意字符串里参数,要加=:。这个跟pascal的有点相似,嘻嘻
//query.setInteger("cid", 2);
// query.setString("cname", "福州大学");
//Query query = session.createQuery("update College c set c.cname=? where c.cid=?");//注意字符串里占位符
//query.setInteger(0, 2);
// query.setString(1, "福州大学");
int count = query.executeUpdate();
ts.commit();
if (count>0){
System.out.println("成功修改了"+count+"行");
}
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
HibernateFactory.closeSession(session);
}
总结:更新表语句和查询表语句是HQL时都是通过session.createQuery(HQL)来接收语句的。查询通过.list()方法返回查出实体对象List集合,而更新语句是通过org.hibernate.Query的executeUpdate()返回整数类型,即更新或删除的数量。数量大于0表示执行成功。更新,保存操作一般都要纳入session的事务管理。
HQL可以通过在HQL语句中用=:指定参数。然后紧接着用query.setString(" 参数串",参数值);给参数赋值。
HQL可以通过在HQL语句中用占位符?来指定参数。然后紧接着用query.setString(" 参数位置",参数值);给参数赋值。参数位置从0开始。
HQL还可以通过setParametor来指定参数。
3.hibernate之主键生成策略
被映射类必须定义对应数据库主键字段,<id>元素定义了该属性到数据库主键字段的映射。<generator>子元素是一个java类的名字,用来为该持久化类的实例生成唯一标识。
Asigned
由程序设置主键。设置必须在save之前完成。
Increment
由hibernate维护生成策略。找出主键字段最大值加1
Indentity
由底层数据库维护的主键生成策略。
需要特定数据库特性支持。mysql或sqlserver
Sequence
Oracle,ProgreSQL数据库支持。底层数据库维护
Native
根据底层数据库特性,自动选择Identity,Sequence.只可以是整型。
UUID
复合主键
需要配置<composite-id>
4.hibernate之主键外键关联
College类还用上面的类.创建一个学生类。
public class Student{
private int sid;
private Stirng
private College college;
set get 方法
}
Student的hbm.xml映射文件如下:
<?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 package="com.ts.pojo" >
<class name="Student" table="t_Student" lazy="true">
<!-- 持久化标识即主键 -->
<id name="stuId" column="sid" type="integer">
<!-- 主键的生成策略 -->
<generator class="identity"></generator>
</id>
<property name="stuName" column="s_name" type="string" length="20"></property>
<many-to-one name="college" class="College" column="cid" unique="true" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
}
一个学校有多个学生。所以学生和学校是多对一的关系。学生类里面有一个学校的属性,对应学生表里就是cid。如果在保存学生信息的时候,给学生指定了学校,当学校表里
又没有这个学校时。会自动在学校表里插入一条纪录。注意many-to-one是在多的一方的映射文件中配置的。这里学生是多的一方,要配置的name是多的一方Student类中的一的一方的关联属性即college,而class是关联属性所指向的类即College。column是多的一方所对应的表中关联的那个字段即cid来关联指向学校表的主键id。
在保存学生信息到学生表里时,也会保存一条学生所在学校的信息到学校表里去。
代码如下:Session session = HibernateFactory.getHibernateSession();
Transaction ts = session.beginTransaction();//纳入事务管理
try {
College c1 = new College();
c1.setCname("zhongsandaxue");
c1.setProvince("guangdong");
c1.setCity("guangzhou");
c1.setCdate(new Date(System.currentTimeMillis()));
Student s1 = new Student();
s1.setStuName("xiaohong");
s1.setCollege(c1);
//session.save(c1);//即使不要这句。只要在Student.hbm.xml中配置了many-to-one。也会在数据库中保存一条C1的信息的。叫级联操作。上面设了cascade为all的。
session.save(s1);
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
HibernateFactory.closeSession(session);
}
一对一,假如一个学校仅且只仅有一个学生。那么我们保持学生的class是Student和映射文件不变。给学校的类增加一个Student类型的属性。然后在College.hbm.xml中配置
one-to-one。如下
<class name="College" table="t_College" lazy="false">
<!-- 持久化标识即主键 -->
<id name="cid" column="cid" type="integer">
<!-- 主键的生成策略 -->
<generator class="identity"></generator>
</id>
<property name="cname" column="c_name" type="string" length="20"></property>
<property name="province" column="c_province" type="string" length="20"></property>
<property name="city" column="c_city" type="string" length="10"></property>
<property name="cdate" column="c_date" type="date"></property>
<one-to-one name="stu" class="Student" cascade="all" property-ref="college"></one-to-one>
</class>
<one-to-one>配置属性name为和本类一对一对应的那个类在本类中的属性。class指和本类一对一对应的那个类,而property-ref指和本类一对一对应的那个类的属性中指定
本类的那个属性名,即主键表中指定外键表中关联本表的那个属性
这样和上面一样,保存student的时候自动保存college.
下面是查询。查询可以实现双向。查出任一方,带出另一方。
Session session = HibernateFactory.getHibernateSession();
Transaction ts = session.beginTransaction();//纳入事务管理
try {
College c1 = null;
c1 = (College)session.get(College.class, 5);
System.out.println(c1);
Student s1 = c1.getStu();//通过查询出College,可以带出和College相关联的Student。反之,先查Student然后也会关联到College.
System.out.println(s1);
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
HibernateFactory.closeSession(session);
}
hibernate之外键关联一对多
一个公司Company有多个员工Emp。对应一个公司表,compid公司ID,compname公司名称。一个员工表empid员工IDempname员工姓名compid员工公司。
现在要新增一条公司信息,公司有三个员工,由于公司表的主键compid是员工表的外键,所以通过配置Company.hbm.xml和Emp.hbm.xml可以实现增加一个公司纪录,同时增加几条员工纪录,当然要提前指定员工属于哪个公司。
公司类
public class Company {
private int compId;
private String compName;
private Set<Emp> set;//注意这个属性是一个由员工作为元素的set集合
员工类
public class Emp {
private int empId;
private String empName;
private Company comp;//注意这里是类类型。指明员工的所在公司。
上面别忘了get/set方法。
Company.hbm.xml
<hibernate-mapping package="com.ts.pojo" >
<class name="Company" table="company">
<!-- 持久化标识即主键 -->
<id name="compId" column="compid" type="integer">
<!-- 主键的生成策略 -->
<generator class="identity" ></generator>
</id>
<property name="compName" column="compname" type="string" length="20"></property>
<set name="set" cascade="all" inverse="true">
<key column="compid"></key>
<one-to-many class="Emp" />
</set>
</class>
</hibernate-mapping>
//重点关注<set>元素的配置,cascade表明所有操作都会级联,要指明set集的子元素key也就是本关联表的关键字段。compid.本表主键。
要指明set集的子元素<one-to-many>的class属性是set集合中元素的类。Emp
Emp.hbm.xml
<hibernate-mapping package="com.ts.pojo" >
<class name="Emp" table="t_Emp">
<!-- 持久化标识即主键 -->
<id name="empId" column="empid" type="integer">
<!-- 主键的生成策略 -->
<generator class="native"></generator>
</id>
<property name="empName" column="empName" type="string" length="20"></property>
<many-to-one name="comp" class="Company" column="compid"></many-to-one>
</class>
</hibernate-mapping>
关注多的一方的<many-to-one>元素。它的name即指表多的一方Emp类的指向一的一方的属性。这里是comp指向Company。class要指明作为一方的那个类。column当然是本表中指向主表的关联字段compid
代码如下:公司增加一条。员工增加三条。
Company c = new Company();
c.setCompName("aaa有限公司");
Set<Emp> set = new HashSet<Emp>();
Emp e1 = new Emp();
e1.setEmpName("shangsan1");
e1.setComp(c);
Emp e2 = new Emp();
e2.setEmpName("shangsan2");
e2.setComp(c);
Emp e3 = new Emp();
e3.setEmpName("shangsan3");
e3.setComp(c);
set.add(e1);
set.add(e2);
set.add(e3);
c.setSet(set);
Session session = null;
Transaction ts = null;
try {
session = HibernateFactory.getHibernateSession();
ts = session.beginTransaction();
session.save(c);
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
hibernate之外键关联多对多<many-to-many>
多对多,举例老师和学生。一个老师可以教多个学生。一个学生可以被多名老师带。定义一个类Teacher一个类Stu
老师类
public class Teacher {
private int tid;
private String tname;
private int tage;
private Set<Stu> stus;
学生类
public class Stu {
private int stuid;
private String stuname;
private int stuage;
private Set<Teacher> teas;
Teacher.hbm.xml配置文件如下
<hibernate-mapping package="com.ts.pojo" >
<class name="Teacher" table="t_Teacher" lazy="true">
<!-- 持久化标识即主键 -->
<id name="tid" column="tid" type="integer">
<!-- 主键的生成策略 -->
<generator class="native">
</generator>
</id>
<property name="tname" column="tname" type="string" length="20"></property>
<property name="tage" column="tage" type="integer"></property>
<set name="stus" cascade="save-update" table="teas_stus">
<key column="teaid"></key>
<many-to-many class="Stu" column="stuid"></many-to-many>
</set>
</class>
</hibernate-mapping>
重点关注配置文件的红色部分及Teacher的private Set<Stu> stus;
既然Teacher用Set集合来存储一个老师教的多个学生。那么自然要在Teacher.hbm.xml中配置set元素了。
<set>里的name属性是指Set集合类型的成员名, 这里是stus。cascade是级联方式。这里是用save-update.就是保存老师信息,同时保存学生信息。如果是all那么会删除老师信息并且也删除该老师带的学生的信息。这样当然是不合理的,因为老师辞职,学生不一定离校,所以不用all.另外还要强调一点的是,还有一个table属性。 这个属性是指定一个中间表的,用来关联老师表和学生表。当出现多对多的关联时,必然会出现一张中间表来进行关联。 这里是teas_stus.
<set>里有一个元素key,就是主键的意思,它的name属性指定与Set集合关联的主键字段,当然是teaid.,多个Stu纪录都指向同一个teaid。
最后配置多对多<many-to-many>它的class属性指向Set集合中元素类型这里是Stu类。column属性指向Set元素的主键stuid
OK让我们一块看下Stu.hbm.xml吧。
<hibernate-mapping package="com.ts.pojo" >
<class name="Stu" table="t_Stu" lazy="true">
<!-- 持久化标识即主键 -->
<id name="stuid" column="sid" type="integer">
<!-- 主键的生成策略 -->
<generator class="native">
</generator>
</id>
<property name="stuname" column="sname" type="string" length="20"></property>
<property name="stuage" column="sage" type="integer"></property>
<set name="teas" cascade="save-update" table="teas_stus">
<key column="stuid"></key>
<many-to-many class="Teacher" column="teaid"></many-to-many>
</set>
</class>
</hibernate-mapping>
这里也要注意红色的配置部分。其它是和老师的配置是一样的。他们刚好构成一个双向多对多的关系。如果不这样配置Stu.hbm.xml那么就是一个一对多的关系。
也就是当增加一个老师信息时,如果指定了这个老师带的几个学生信息,并把学生信息存到Set集合中,保存老师信息时,自动保存该老师的学生信息。反之亦然。
代码示例如下:
public static void save_tea_stu(){
Teacher t1 = new Teacher();
t1.setTname("MrZhang");
t1.setTage(40);
Teacher t2 = new Teacher();
t2.setTname("MrLi");
t2.setTage(40);
Teacher t3 = new Teacher();
t3.setTname("MrLiu");
t3.setTage(40);
Stu s1 = new Stu();
s1.setStuname("xiaohonng");
s1.setStuage(12);
Stu s2 = new Stu();
s2.setStuname("xiaojun");
s2.setStuage(15);
Stu s3 = new Stu();
s3.setStuname("xiaokai");
s3.setStuage(13);
Stu s4 = new Stu();
s4.setStuname("xiaochen");
s4.setStuage(16);
Set<Stu> set1 = new HashSet<Stu>();
set1.add(s1);
set1.add(s2);
set1.add(s3);
t1.setStus(set1);
Session session = null;
Transaction ts = null;
try {
session = HibernateFactory.getHibernateSession();
ts = session.beginTransaction();
session.save(t1);
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
}
补充:在POJO类中,多的一方作为一的一方的成员时,除了用Set作为集合来接收多的一方的数据外还可以使用List集合来接收多的一方数据。例如下:
public class Teacher {
private int tid;
private String tname;
private int tage;
/*private Set<Stu> stus;
public Set<Stu> getStus() {
return stus;
}
public void setStus(Set<Stu> stus) {
this.stus = stus;
}*/
private List<Stu> stus;
如果这样的类,那Teacher.hbm.xml也要按下面来设置才行。
<hibernate-mapping package="com.ts.pojo" >
<class name="Teacher" table="t_Teacher" lazy="true">
<!-- 持久化标识即主键 -->
<id name="tid" column="tid" type="integer">
<!-- 主键的生成策略 -->
<generator class="native">
</generator>
</id>
<property name="tname" column="tname" type="string" length="20"></property>
<property name="tage" column="tage" type="integer"></property>
<!--
<set name="stus" cascade="save-update" table="teas_stus">
<key column="teaid"></key>
<many-to-many class="Stu" column="stuid"></many-to-many>
</set>
-->
<list name="stus" cascade="save-update" table="teas_stus">
<key column="teaid"></key>
<index column="stuid_index"></index>
<many-to-many class="Stu" column="stuid"></many-to-many>
</list>
</class>
</hibernate-mapping>
还要着重关注红色的配置部分。这个跟Set也差不多,不同之处有二:一是List配置文件要配索引,二是List配置的index元素的column不能字段或列名重名。
List<Stu> list1 = new ArrayList<Stu>();
list1.add(s1);
list1.add(s2);
list1.add(s3);
t1.setStus(list1);
Hibernate之由表反向生成类和映射文件
这个功能在MyEclpse的最近几个版本上都可以直接使用。使用MyEclipse Database Explorer视图,在里面连接好数据库,建好表和字段,选择表后右键反向生成。如果是Eclips需要下载相关插件。
Hibernate之类与类之间Has a的关系
例如Person类里有一个成员是Address。即人有自己的地址。而地址也是一个类。这种情况还是很觉见的。
Person类
public class Person {
private int pid;
private String pname;
private Address address;
Address类
public class Address {
private String province;
private String city;
private String street;
Person.hbm.xml的映射文件
<hibernate-mapping package="com.ts.pojo" >
<class name="Person" table="t_person">
<!-- 持久化标识即主键 -->
<id name="pid" column="pid" type="integer">
<!-- 主键的生成策略 -->
<generator class="identity" ></generator>
</id>
<property name="pname" column="pname" type="string" length="20"></property>
<component name="address" class="Address">
<property name="province" column="province" type="string" length="20"></property>
<property name="city" column="city" type="string" length="20"></property>
<property name="street" column="street" type="string" length="20"></property>
</component>
</class>
</hibernate-mapping>
重点关注红色部分。配一个component的元素。属性name就是该component在主类Person中的成员名。classs指向该成员的类类型。
component下的有三个子属性,分别是类Addrss的三个成员。要分别指定每个成员对应的数据库的column即列名。还有数据类型,这样才能在创建表时创建相应字段,生成数据时自动关联到对应列中去。
public static void generatePerson(){
Person p= new Person();
p.setPname("gaoyang");
Address addr = new Address();
addr.setProvince("henane");
addr.setCity("anyang");
addr.setStreet("gaugngmingdadao45hao");
p.setAddress(addr);
Session session = null;
Transaction ts = null;
try {
session = HibernateFactory.getHibernateSession();
ts = session.beginTransaction();
session.save(p);
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
}
执行脚本如下
Hibernate:
insert
into
t_person
(pname, province, city, street)
values
(?, ?, ?, ?)
Hibernater的继承关系使用
新建两个类Farmer和Worker继承自Person
public class Farmer extends Person{
private String farm;
}
public class Worker extends Person{
private String factory';
}
不要忘了getset方法
Person.hbm.xml配置文件
hibernate-mapping package="com.ts.pojo" >
<class name="Person" table="t_person">
<!-- 持久化标识即主键 -->
<id name="pid" column="pid" type="integer">
<!-- 主键的生成策略 -->
<generator class="identity" ></generator>
</id>
<discriminator column="category" type="string" length="20"></discriminator>
<property name="pname" column="pname" type="string" length="20"></property>
<component name="address" class="Address">
<property name="province" column="province" type="string" length="20"></property>
<property name="city" column="city" type="string" length="20"></property>
<property name="street" column="street" type="string" length="20"></property>
</component>
<subclass name="Farmer" discriminator-value="STUDENT">
<property name="farm" column="farm" type="string" length="20"></property>
</subclass>
<subclass name="Worker" discriminator-value="WORKER">
<property name="factory" column="factory" type="string" length="30"></property>
</subclass>
</class>
</hibernate-mapping>
注意discriminator要紧接着id放,否则会报错,column会在Person对应的表里增加的字段。默认值就是下面<subclass>中的discriminator-value的值,不同的子类可以设不同
<subclass的name指定子类的类名discriminator-value也在这个地方>子元素指定子类独有的属性<property>name是子类中的成员名,column是指在表中生成的字段,type指定该字段的数据类型
生成数据代码如下
public static void generatePerson(){
Address addr = new Address();
addr.setProvince("henane");
addr.setCity("anyang");
addr.setStreet("gaugngmingdadao45hao");
Worker w = new Worker();
w.setPname("work");
w.setFactory("Factory");
w.setAddress(addr);
Farmer f = new Farmer();
f.setPname("farmer");
f.setFarm("farm");
f.setAddress(addr);
Session session = null;
Transaction ts = null;
try {
session = HibernateFactory.getHibernateSession();
ts = session.beginTransaction();
session.save(w);
session.save(f);
ts.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
}