----------------------------Hibernate 简介 ----------------------------
模型不匹配 ( 阻抗不匹配 )
Java 面向对象语言,对象模型,其主要概念有:继承、关联、多态等;
数据库是关系模型,其主要概念有:表、主键、外键等。
想用 Java 来操作关系型数据库的时候,就会有阻抗不匹配的情况出现
解决办法
使用 JDBC 手工转换。
使用 ORM(Object Relation Mapping 对象关系映像 ) 框架来解决,主流的 ORM 框架有 Hibernate 、 TopLink 、 OJB 。
衔接对象和关系模型完全是由映像文件办到的
-------------------------------- 安装配置 ------------------------------
下载地址 http://www.hibernate.org ,本教程使用 3.2.5 。
将下载目录 /hibernate3.jar 和 /lib 下的 hibernate 运行时必须的包加入 classpath 中:
antlr.jar,
cglib.jar,
asm.jar,
commons-collections.jar,
commons-logging.jar,
jta.jar,
dom4j.jar
配置文件 hibernate.cfg.xml 和 hibernate.properties , XML 和 properties 两种,这两个档的作用一样,提供一个即可,推荐 XML 格式,下载目录 /etc 下是示例配置文件。
可以在配置文件指定:
数据库的 URL 、用户名、密码、 JDBC 驱动类、方言等。
启动时 Hibernate 会在 CLASSPATH 里找这个配置文件。
映射文件 (hbm.xml ,对象模型和关系模型的映像 ) 。在 /eg 目录下有完整的 hibernate 示例。
--------------------------- Domain Object 限制 -------------------------
1. 默认的构造方法 ( 必须具有的一个无参的默认构造方法 ) 。
如果含有一个有参的构造方法的,这样的 JavaBean , Hibernate 是没有办法给管理的
2 有无意义的标示符 id (主键) ( 可选 )
可以在 JavaBean 中没有主键 id ,但是没有的话,有一些方法就会受到一些限制,就不能完全发挥 hibernate 的一些特性,这样操作起来就会方便一些
3 非 final 的,对懒载入有影响(可选)
因为加上 final 的类,会不能被继承等限制
Domain Java Object(User)
public class User {
private int id;
private String name;
private Date birthDay;
//getter setter…
}
--------------------------- 一个简单的映射档 -------------------------
hbm.xml
<?xml version="1.0"?>
<hibernate-mapping package=“cn.itcast.domain">
<class name="User" table="user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="birthday”/>
</class>
</hibernate-mapping>
Hibernate 的映像文件就是用于说明 java 对象与哪个表中的记录相对应,以及 Java 对象中的各个属性分别对应表中的哪一列,不同性质的属性 ( 例如主键和普通属性 ) 用不同的标签来映像,如果 Java 对象中的某个属性不需要存储在数据库中,那么在 Hibernate 映像文件就不需要配置这个属性 !
------------------------------ 简化 Session----------------------------
创建一个工具类 HibernateUtil ,用来打开和关闭 session ,以简化每次重复的操作
public final class HibernateUtil {
private static SessionFactory sessionFactory;
private static ThreadLocal session = new ThreadLocal();
private HibernateUtil() {
}
static {
Configuration cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
public static void closeSession() {
Session s = (Session) session.get();
if (s != null ) {
s.close();
session.set( null );
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() {
return sessionFactory.openSession();
}
}
-------------------------Session 的几个主要方法 -------------------------
1.save,persist 保存数据, persist 在事务外不会产生 insert 语句。
2.delete, 删除对象
3.update, 更新对象,如果数据库中没有记录,会出现异常。
4.get, 根据 ID 查,会立刻访问数据库。
5.Load ,根据 ID 查, ( 返回的是代理,不会立即访问数据库 ) 。
6.saveOrUpdate,merge( 根据 ID 和 version 的值来确定是 save 或 update), 调用 merge 你的物件还是托管的。
7.lock( 把对象变成持久对象,但不会同步对象的状态 ) 。
---------------------------save-persist 方法 --------------------------
这两个方法都表示 insert ,效果是一样的,但是在有关事务的操作上有区别
如果你不开启事务:
save 方法是先执行插入操作,由于没有提交,然后再回滚数据,数据库中也不会含有该记录
但是如果使用了 show_sql 特性,那么会显示出来 insert 的插入语句
persist 方法是根本就不执行,即使使用了 show_sql 特性也不会看到会有 insert 语句的
static void addUser(User user) {
Session s = null ;
Transaction tx = null ;
try {
s = HibernateUtil.getSession(); // 通过 HibernateUtil 自定义的工具类来获取 session
tx = s.beginTransaction(); // 开启事务
s.save(user);
//s.persist(user);
tx.commit(); // 提交
} catch (HibernateException e) {
if (tx != null )
tx.rollback();
throw e;
} finally {
if (s != null )
s.close();
}
}
-----------------------------get-load 方法 ----------------------------
static User getUser( int id) {
Session s = null ;
try {
s = HibernateUtil.getSession();
Query q = s.createQuery( "from User where id=:id" );
q.setInteger( "id" , id);
q.uniqueResult();
//User user = (User) s.get(User.class, id);
/* 查询操作,根据主键 id 的值获取相应的一条记录 ( 返回一个对象 ),
User.class 表示通过反射机制来找到相应的 xxx.hbm.xml 文件并匹配 */
User user1 = (User) s.load(userClass, id);
/* load 方法虽然写法上与 get 方法很相似,但是他不是立刻访问数据库,
返回的对象第一次使用的时候才去真正的加载数据库
例如:
User user1 = (User) s.load(userClass, id);
return user1;
这样执行以后如果 user1 并没有去获取某个属性的值,那么返回的 user1 对象里边不会包含任何数据
只有当: User user1 = (User) s.load(userClass, id);
System.out.println(user1.getName());
return user1;
这样返回的 user1 对象,才包含了查询数据库以后所得到的数据
这个技术叫做懒加载,它不是真正的访问数据库,而是第一次使用时才去访问数据库的
load 方法所返回的对象永远不会为空 (null), 永远不要写这样的代码
if(user1==null){
}
if(user1!=null){
}
即使参数主键 id 的值不存在,返回的对象也不会为空
因为前面讲过定义的 Javabean 不能为 final , load 方法所加载的类会通过继承该 Javabean
而得到一个子类,这个类是直接 new 出来的
查看 load 方法所返回的 Javabean 子类的名称:
System.out.println(user1.getClass().getName());
*/
Hibernate.initialize(user1);
System.out.println(user1.getName());
return user1;
} finally {
if (s != null )
s.close();
}
}
-----------------------saveOrUpdate-merge 方法 ------------------------
saveOrUpdate 方法根据 ID 和 version 的值来自动判断是 save 或 update
如果 javabean 中包含有 id ,说明对象是脱管状态,就执行 update ,
反之,如果不包含 id ,说明瞬时状态,执行 save
调用 saveOrUpdate 方法后,对象会变成持久的
调用 merge 方法后,对象还是脱管的
-------------------------------- 对象状态 ------------------------------
1. 瞬时 (transient) :数据库中没有数据与之对应,超过作用域会被 JVM 垃圾回收器回收,一般是 new 出来且与 session 没有关联的对象。
2. 持久 (persistent) :数据库中有数据与之对应,当前与 session 有关联,并且相关联的 session 没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库 (hibernate 能检测到 ) 。
3. 脱管 (detached)( 也可称为游离态 ) :数据库中有数据与之对应,但当前没有 session 与之关联;托管对象状态发生改变, hibernate 不能检测到。
public class Test1 {
// 考虑持久化物件的生命周期
public static void main(String[] args) {
Books book= new Books( "java" , "Java2 学习 11" , 12.36f, "123.jpg" );
// 不包含任何的数据库的信息 , 只是数据的封装
// 此时 book 对象在内存中自由的存在 , 他与数据库无关 , 随程序的执行而消失 (Transient)
// 瞬态向持久态转换
Session session=HibernateSessionFactory.getSession();
Transaction tx = session.beginTransaction();
session.save(book);
// 此时 book 对象由 Hibernate 管理 , 此时 book 对象在 hibernate 管理内存 (persistent)
System. out .println(book.getBid());
tx.commit();
// 持久化 , 保存在数据库中 , 管理器中应该含有一个 book 对象 ( 该对象和数据库中的内容一致的 )
book.setBname( "C#" );
// 在管理器中修改了 book 对象 ? : 数据库中的数据改变吗 改变,因为是持久化
tx.commit();
// 再一次的持久化
//?: 执行了 Update 操作 , 修改的是那里的数据
session.close();
// 关闭了 session 的管理
System. out .println(book.getBid());
// 游离态 (Detached)
//? Transient 和 Detached 的区别
/*
Detached 状态的对象可以再次与某个 session 进行关联 , 再次变成 persistent
*/
session=HibernateSessionFactory.getSession();
session.saveOrUpdate(book); // 再次发生持久 ,id 信息保存的
session.beginTransaction().commit();
session.close();
//?Transient 和 Detached 都与 Hibernate 的容器无关 , 有什么差异
//Detached: 包含一些额外的信息 ( 持久化的信息 )
}
}
------------------------------HQL --------------------------
1. HQL(Hibernate Query Language)
面向对象的查询语言,与 SQL 不同, HQL 中的对象名是区分大小写的(除了 JAVA 类和属性其他部分不区分大小写); HQL 中查的是对象而不是和表,并且支持多态; HQL 主要通过 Query 来操作, Query 的创建方式:
Query q = session.createQuery(hql);
from Person
from User user where user.name=:name
from User user where user.name=:name and user.birthday < :birthday
2. hql 的命名参数
这个是普通的写法
s = HibernateUtil.getSession();
String hql = "from User as user where user.name=?;// from Object
Query query = s.createQuery(hql);
query.setString(0, name);
0 这个索引号必须要和 ? 的位置一一对应,这种对应的维护起来可能就稍微麻烦一些
所以就有一种更好的方式,叫做命名参数
String hql = "from User as user where user.name=:n";
query.setString("n", name);
就是把 ? 给起个名字, query.setString("n", name); 这样对应的就是名字,而不依赖于位置了
3. 分页查询
query.setFirstResult(200);// 第一条记录从哪开始取
query.setMaxResults(10);// 取多少条
这两个方法是可以实现分页的
这种分页的好处是可以实现跨数据库,可移植的
Mysql 用 limit 分页, sqlServer 用 top , Oracle 用 number ,这些关键词都是不一样的
那么 hibernate 是通过哪个参数知道要用哪个分页的呢
在 hibernate.cfg.xml 配置文件中配置
<property name="dialect">org.hibernate.dialect.MySQLDialect
</property>
4. 查询只取一个对象
User u = (User) query.uniqueResult();
结果集如果确定只有一行,就可以用这个方法,如果返回的是多行,就会报异常了
--------------------------------Criteria-----------------------------
1. Criteria
Criteria 是一种比 HQL 更面向对象的查询方式; Criteria 的创建方式:
Criteria crit = session.createCriteria(DomainClass.class);
简单属性条件如: criteria.add(Restrictions.eq(propertyName, value)),
criteria.add(Restrictions.eqProperty(propertyName,otherPropertyName))
--------------------------- 自定义主键的生成 ----------------------------
产生一些有规律的主键 ( 唯一值 id), 要求 :yyyymmdd+ 流水号
1. 创建表
create table orders(
id varchar(50) primary key,
price float not null
)
2. 创建 Orders 表的 javabean ,及配置文件 Orders.hbm.xml
3. 向 hibernate.cfg.xml 配置文件中添加对类配置文件的映射
4. 创建一个工具类 OrderPKGen ,该类实现了 IdentifierGenerator 接口
public class OrderPKGen implements IdentifierGenerator {
// 自定义主键的生成
// 创建一个日期的输出格式
private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd-" );
// 这里一定得是小写 y ,大写 M ,小写 d ,否则其表示的意义会变化
public Serializable generate(SessionImplementor arg0, Object arg1) throws HibernateException {
// 生成主键的方法,需要使用 hibernate 所提供的身份生成器的接口
String dataPart= sdf .format( new Date());
return dataPart+System.currentTimeMillis ();
// 返回的逐渐值为 当前的年月日 + 系统当前时间的毫秒数
}
}
5. 写一个测试类
public class IDTest {
public static void main(String[] args) {
Configuration cfg= new Configuration ();
cfg.configure();
SessionFactory sf=cfg.buildSessionFactory();
Session s=sf.openSession();
Transaction tx=s.beginTransaction();
Orders o= new Orders();
o.setDesc( 27.8f );
s.save(o);
System. out .println(o.getId()+ " " +o.getPrice());
tx.commit();
s.close();
}
}
--------------------------------- 多对一 -------------------------------
多对一 (Employee - Department)
ER 图
一个部门包含了多个员工,但从员工的角度来看问题,就是多对一了
1. 创建数据表
create table Department(
id int primary key,
name varchar(10)
);
create table Employee(
id int primary key,
name varchar(10),
depart_id int
);
2. 创建 javabean Department 类和 Employee 类
Department.java
public class Department {
private int id ;
private String name ;
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
}
Employee.java
public class Employee {
private int id ;
private String name ;
private Department depart ;
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
public Department getDepart() {
return depart ;
}
public void setDepart(Department depart) {
this . depart = depart;
}
}
3. 创建配置文件
Department.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Department" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" />
</ class >
</ hibernate-mapping >
Employee.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Employee" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" />
<!-- 我们用 property 所映像的属性只能是简单属性,字符串或者是整型的 -->
<!-- 根据对象之间的关系,来判断使用什么标签 -->
< many-to-one name = "depart" column = "depart_id" ></ many-to-one >
<!--<many-to-one> 专门用来描述多对一的关系 ,
name 表示多对一关系中 n 所定义的 1 的对象名 ,
column 表示与数据表中所对应的字段 , 若不设置该属性,默认的字段名为该对象名
该字段默认对应 1 的主键 id, 如果 1 的主键是 name 属性或者其他
而不是 id, 则需要使用 property-ref="name" 属性来设置对应 1 的主键 -->
</ class >
</ hibernate-mapping >
4. 向 hibernate.cfg.xml 配置文件中添加对类配置文件的映射
< mapping resource = "com/domain/Department.hbm.xml" />
< mapping resource = "com/domain/Employee.hbm.xml" />
5. 测试类
public class ManyToOne {
public static void main(String[] args) {
Session s=HibernateUtil.getSession ();
Transaction tx=s.beginTransaction();
Department depart= new Department();
depart.setName( " 会计 " );
Employee emp= new Employee();
emp.setName( "Shiki" );
emp.setDepart(depart);
s.save(depart);
s.save(emp);
tx.commit();
s.close();
}
}
这里有个小插曲,执行的时候报错了
Could not find the class,Problem will exit
一开始不明白,后来上网查查原来是因为我们使用高版本的 JDK 编译的 Java class 档试图在较低版本的 JVM 上运行,所报的错误。 因为,高版本的 JDK 生成的 class 档使用的格式,可能与低版本的 JDK 的 .class 文件格式不同。这样,低版本的 JVM 无法解释执行这 个 .class 文件,会抛出 Could not find the main class.program will exit 不支持的 Class 版本错误。 这个错误尤其在 JDK5 与 JDK5 之前的版本上表现明显。因为, JDK5 在 Java 语法上作了不少增强,引入了一 些新的 .class 文件的元素,导致旧版本的 JVM 无法解释、执行 .class 文件。例如这个类并没有使用任何 JDK5 的新元素,但是 JDK1.5 生成 的 .class 文件, JDK1.4 及其以前的 JVM 都无法辨认。
解决方法:
1. 弄清楚该程序是在哪个 JDK 版本下开发的
2. 在 myEclipse 菜单 ->Windows->Proferences->java-> Installs Jres 重新设置 JDK 路径,改为开发程序时的 JDK 版本(我做到这一步就可以了!我的 eclipse 是 7.0 , jdk 是 6u13 的。)
3. Windows->Proferences->java->Compiler ,将 compiler compliance level 设置为与上面相同的 JDK
我所使用的 hibernate 的版本为 3.5.4 ,错误不知为何
--------------------------------- 一对多 -------------------------------
一个部门包含了多个员工,从部门的角度来看问题,就是一对多了
在 Department 檔中,增加一个泛型为 Employee 类型的 Set 集合
private Set<Employee> emps = new HashSet<Employee>();
用来存储多个员工对象,也就是一个部门包含多个员工
修改 1 方的配置文件
< set name = "employees" >
< key column = "depart_id" /> <!--column 描述 n 的外键,用于与 1 集合中的数据对应上 -->
< one-to-many class = "Employee" /> <!--class 指定集合中存放数据的类型 -->
</ set >
--------------------------------- 一对一 -------------------------------
基于主键的一对一 (Person - IdCard)
ER 图
一个人有一个身份证卡,从两边来看都是一对一的关系
以 Person 为主表, id 为主键,从表 id_card 表的 id 为外键,在配置文件中设置 id_card 的外键引用
1. 创建表
在 hibernate.cfg.xml 配置文件中,设置
< property name = "hbm2ddl.auto" > update </ property >
自动创建表格,并自动更新数据
2. Javabean
创建 Person 类和 ID_Card 类,因为是一对一关系,所以各自的类中都包含一个对应类的实例对象
Person.java
public class Person {
private int id ;
private String name ;
private int age ;
private ID_Card card ;
public int getAge() {
return age ;
}
public void setAge( int age) {
this . age = age;
}
public ID_Card getCard() {
return card ;
}
public void setCard(ID_Card card) {
this . card = card;
}
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
}
ID_Card.java
public class ID_Card {
private int id ;
private String name ;
private Person person ;
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
public Person getPerson() {
return person ;
}
public void setPerson(Person person) {
this . person = person;
}
}
3. 配置文件
Person.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Person" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" />
< property name = "age" />
< one-to-one name = "card" ></ one-to-one > <!-- 一对一关系,对应在 Person 类中所定义的 card 对象 -->
</ class >
</ hibernate-mapping >
ID_Card.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "ID_Card" >
< id name = "id" >
< generator class = "foreign" >
< param name = "property" > person </ param >
</ generator >
</ id >
< property name = "name" />
< one-to-one name = "person" constrained = "true" ></ one-to-one >
<!-- 对应 person 对象, constrained 属性表示创建表时带有外键关联,使用 show create table xxx 可查看建表语句 -->
</ class >
</ hibernate-mapping >
4. 向 hibernate.cfg.xml 配置文件中添加对类配置文件的映射
5. 测试类
OneToOne.java
public class OneToOne {
public Person add(){
Session s=HibernateUtil.getSession ();
Transaction tx=s.beginTransaction();
Person p= new Person();
p.setName( "1 号 " );
p.setAge(22);
ID_Card card= new ID_Card();
card.setName( "120101xxxxxxxxxxxx" );
//p.setCard(card);
card.setPerson(p);
s.save(p);
s.save(card);
tx.commit();
s.close();
return p;
}
public void query( int id){
Session s=HibernateUtil.getSession ();
Transaction tx=s.beginTransaction ();
// 查询主对象 person
//Person p=(Person) s.get(Person.class,id);
//System.out.println(p.getCard().getName());
// 查询从对象 id_card
ID_Card card=(ID_Card)s.get (ID_Card. class ,id);
System. out .println(card.getPerson().getName());
tx.commit ();
s.close ();
}
public static void main(String[] args) {
OneToOne o= new OneToOne();
Person p=o.add();
o.query(p.getId());
}
}
基于外健的一对一 (Person - IdCard)
Person 是 OneToOne ,而 id_card 采用 ManyToOne ,也就是一个 Person 可以有多个 id_card, 但是从 id_card 中来限制其唯一性,就演变成唯一的多对一,其实就变成了一对一了
修改配置文件及表结构
Person.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Person" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" />
< property name = "age" />
< one-to-one name = "card" property-ref = "person" />
<!--property-ref 用于指定关联类的一个属性,这个属性将会和本外键相对应 -->
</ class >
</ hibernate-mapping >
ID_Card.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "ID_Card" >
< id name = "id" >
< generator class = "foreign" >
< param name = "property" > person </ param >
</ generator >
</ id >
< property name = "name" />
< many-to-one name = "person" column = "person_id" unique = "true" not-null = "true" />
</ class >
</ hibernate-mapping >
--------------------------------- 多对多 -------------------------------
多对多 (teacher - student)
在操作和性能方面都不太理想,所以多对多的映射使用较少,实际使用中最好转换成一对多的对象模型; Hibernate 会为我们创建中间关联表,转换成两个一对多。
ER 图
Teacher 有多个 student , student 也可以有多个教师
多对多就是由两个一对多组合而成 ( 数据库由 hibernate 自动生成 )
1. Javabean
Teacher.java
public class Teacher {
private int id ;
private String name ;
private Set<Student> student = new HashSet<Student>();
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
public Set<Student> getStudent() {
return student ;
}
public void setStudent(Set<Student> student) {
this . student = student;
}
}
Student.java
public class Student {
private int id ;
private String name ;
private Set<Teacher> teacher = new HashSet<Teacher>();
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
public Set<Teacher> getTeacher() {
return teacher ;
}
public void setTeacher(Set<Teacher> teacher) {
this . teacher = teacher;
}
}
2. 配置文件
Teacher.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Teacher" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" />
< set name = "student" table = "teacher_student"
cascade = "all" inverse = "true" >
<!--
cascade 的属性有:
1.all :所有操作情况下都进行级联操作,即 save-update 和 delete
2.none :所有情况下均不进行级联操作。这是默认值。
3.save-update :执行 save/update/saveOrUpdate 时进行级联操作。
4.delete :在执行 delete 操作的时候进行级联操作。
5.all-delete-orphan :当一个对象节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中, student 包含多个 book ,当在对象关系中删除一个 book 时,此 book 即成为孤儿节点。
inverse 属性值 true 或者 false ,默认值 false( 即默认己方维护关联关系 )
false: 代表有己方来维护关系, true 代表由对方来维护关联关系。
在一个关系中,只能有一方来维护关系,否则会出问题;同时也必须有一方维护关系,否则会出现双方互相推卸责任,谁也不管。
-->
< key column = "teacher_id" ></ key >
< many-to-many class = "Student" column = "student_id" >
</ many-to-many >
</ set >
</ class >
</ hibernate-mapping >
Student.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Student" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" />
< set name = "teacher" table = "teacher_student"
cascade = "all" inverse = "false" >
< key column = "student_id" ></ key >
< many-to-many class = "Teacher" column = "teacher_id" >
</ many-to-many >
</ set >
</ class >
</ hibernate-mapping >
3. 向 hibernate.cfg.xml 配置文件中添加对类配置文件的映射
4. 测试类
public void add(){
Session s=HibernateUtil.getSession ();
Transaction tx=s.beginTransaction();
Teacher teacher1= new Teacher();
teacher1.setName( "1 号教师 " );
Teacher teacher2= new Teacher();
teacher1.setName( "2 号教师 " );
Student student1= new Student();
student1.setName( "1 号学生 " );
Student student2= new Student();
student2.setName( "2 号学生 " );
Student student3= new Student();
student2.setName( "3 号学生 " );
teacher1.getStudent().add(student1);
teacher1.getStudent().add(student2);
teacher2.getStudent().add(student3);
student1.getTeacher().add(teacher1);
student2.getTeacher().add(teacher1);
student3.getTeacher().add(teacher2);
s.save(teacher1);
s.save(teacher2);
// 配置文件中, inverse="true" 表示由对方来维护关联关系,谁维护就保存谁
tx.commit();
s.close();
}
public void query( int id){
Session s=HibernateUtil.getSession ();
Transaction tx=s.beginTransaction();
// 查询主对象 person
//Person p=(Person) s.get(Person.class,id);
//System.out.println(p.getCard().getName());
// 查询从对象 id_card
ID_Card card=(ID_Card)s.get(ID_Card. class ,id);
System. out .println(card.getPerson().getName());
tx.commit();
s.close();
}
public static void main(String[] args) {
ManyToMany m= new ManyToMany();
m.add();
m.query(1);
}
}
--------------------------component 组件映像 ---------------------------
组件映像,也称为关联映射
一个 javabean 的属性不仅由数据组成,其中也包括了对象类型,也就是另一个 javabean 实体的对象
关联的属性是个复杂类型的持久化类,但不是实体,即:数据库中没有表与该属性对应,但该类的属性要之久保存的。所以就使用组件映像,但不是唯一的方法,也可以使用自定义类型的方式来处理,但是更复杂,更麻烦
但有的时候就必须要使用自定义类型,例如:类里边有两个属性,想要放入到数据库中表的三个列里边,这样的需求组件映像就不行了,只能使用自定义类型
1. 创建表
create table users(
id int auto_increment primary key,
name varchar(20) not null,
zipcode varchar(20) not null,
tel varchar(20) not null
)
2. 创建 JavaBean
public class Users implements Serializable{
private int id ;
private String name ;
private Address address ; // 此时表中的部分数据,进行了二次封装 , 是一个组件
public Address getAddress () {
return address ;
}
public void setAddress(Address address) {
this . address = address;
}
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getName() {
return name ;
}
public void setName(String name) {
this . name = name;
}
}
Users 类中还包含了一个 Address 对象
public class Address {
private int id ;
private String zipcode ;
private String tel ;
public int getId() {
return id ;
}
public void setId( int id) {
this . id = id;
}
public String getTel() {
return tel ;
}
public void setTel(String tel) {
this . tel = tel;
}
public String getZipcode() {
return zipcode ;
}
public void setZipcode(String zipcode) {
this . zipcode = zipcode;
}
}
3. 创建配置文件 Users.hbm.xml
< hibernate-mapping package = "com.domain" >
< class name = "Users" table = "users" >
< id name = "id" type = "integer" >
< generator class = "native" />
</ id >
< property name = "name" type = "string" not-null = "true" />
< component name = "address" >
<!-- 在配置文件中,视 Users 类中的 Address 为一个组件, name 表示 Users 中所包含的对象名 -->
< property name = "zipcode" type = "string" ></ property > <!-- 设置 Address 中包含的属性 -->
< property name = "tel" type = "string" ></ property >
</ component >
</ class >
</ hibernate-mapping >
4. 向 hibernate.cfg.xml 配置文件中添加对类配置文件的映射
5. 测试类
public class ComponentTest {
public static void main(String[] args) {
Configuration cfg= new Configuration();
cfg.configure();
SessionFactory sf=cfg.buildSessionFactory();
Session s=sf.openSession();
Transaction tx=s.beginTransaction();
Address a= new Address();
a.setZipcode( "300193" );
a.setTel( "12345678901" );
Users u= new Users();
u.setName( "Shiki" );
u.setAddress (a); // 向 Users 对象中添加 Address 对象
s.save(u);
System. out .println(u.getId()+ " " +u.getAddress().getTel());
tx.commit();
s.close();
}
}
-----------------------Hibernate 中使用的集合类型 -----------------------
在 Hibernate 中可以使用多种类型来保存对象实例,写法基本相同,只是配置文件的写法不同
1. Set 不重复
< set name = "emps" inverse = "true" >
< key column = "depart_id" />
< one-to-many class = "Employee" />
</ set >
2. List 带有顺序
< list name = "emps" >
< key column = "depart_id" />
< list-index column = "order_col" />
< one-to-many class = "Employee" />
</ list >
3. Bag 类中仍然使用 List ,在配置文件中使用 bag ,表示不带有顺序的 List
< bag name = "emps" >
< key column = "depart_id" />
< one-to-many class = "Employee" />
</ bag >
4. Map
< map name = "emps" >
< key column = "depart_id" />
< map-key type = "string" column = "name" />
< one-to-many class = "Employee" />
</ map >
集合映像 (set, list, array,bag, map)
这些集合类都是 Hibernate 实现的类和 JAVA 中的集合类不完全一样, set,list,map 分别和 JAVA 中的 Set,List,Map 接口对应, bag 映射成 JAVA 的 List ;这些集合的使用和 JAVA 集合中对应的接口基本一致;在 JAVA 的实体类中集合只能定义成接口不能定义成具体类, 因为集合会在运行时被替换成 Hibernate 的实现。
集合的简单使用原则:大部分情况下用 set ,需要保证集合中的顺序用 list ,想用 java.util.List 又不需要保证顺序用 bag 。
--------------------------- 关联关系的级联操作 ---------------------------
cascade 和 inverse (Employee – Department)
Casade 用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用的 cascade:
none,all,save-update ,delete,lock,refresh,evict,replicate,persist,
merge,delete-orphan(one-to-many)
一般对 many-to-one,many-to-many 不设置级联,在 <one-to-one> 和 <one-to-many> 中设置级联。
inverse 表“是否放弃维护关联关系 ”( 在 Java 里两个对象产生关联时,对数据库表的影响 ) ,在 one-to-many 和 many-to-many 的集合定义中使用, inverse=”true” 表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成 false (注意 hibernate 的缺省值是 false )。
one-to-many 维护关联关系就是更新外键。 many-to-many 维护关联关系就是在中间表增减记录。
注 : 配置成 one-to-one 的对象不维护关联关系
Cascade 与 inverse 这两个属性都用于一对多或者多对多的关系中。而 inverse 特别是用于双向关联关系,在单向关联关系中我们并不需要。
cascade 代表是否执行级联操作, inverse 代表是否由己方维护关系。
cascade :
cascade 的属性有:
1·all :所有操作情况下都进行级联操作,即 save-update 和 delete
2·none :所有情况下均不进行级联操作。这是默认值。
3·save-update :执行 save/update/saveOrUpdate 时进行级联操作。
4·delete :在执行 delete 操作的时候进行级联操作。
5·all-delete-orphan :当一个对象节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中, student 包含多个 book ,当在对象关系中删除一个 book 时,此 book 即成为孤儿节点。
Inverse:
inverse 属性值 true 或者 false ,默认值 false( 即默认己方维护关联关系 )
false: 代表有己方来维护关系, true 代表由对方来维护关联关系。
在一个关系中,只能有一方来维护关系,否则会出问题;同时也必须有一方维护关系,否则会出现双方互相推卸责任,谁也不管。
---------------------inverse 属性的作用与原理分析 ------------------------
-------------------- 继承映射——整个继承树映射到一张表 --------------------
Hibernate 如何将继承结构应用到数据库里
还以员工这个类来做例子,
假设员工有 Skiller 和 Sales 两个子类,如何将他们实现到数据库里
虽然员工有技术或者销售两种类型的,但我们可以把这些都放在员工一张表中,那么如何来区分员工呢 ? 我们可以给员工增加若干个字段,其中有一个 ” 类型 ” 字段,来区分员工是技术还是销售,然后在 skill 和 sell 字段中表现出来技能是什么或者是销售额是多少
1. 创建 Skiller 和 Sales 类,并分别继承 Employee 类
public class Skiller extends Employee {
private String skill ;
public String getSkill() {
return skill ;
}
public void setSkill(String skill) {
this . skill = skill;
}
}
public class Sales extends Employee {
private int sell ;
public int getSell() {
return sell ;
}
public void setSell( int sell) {
this . sell = sell;
}
}
2. 修改 Employee.hbm.xml 文件
-------------------------------- 缓存 ------------------------------
缓存的作用主要用来提高性能,可以简单的理解成一个 Map ;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。
一级缓存, Session 级共享。
save,update,saveOrUpdate,load,get,list,iterate,lock 这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用 evict,clear 方法清除缓存中的内容。