一.Hibernate4是什么
1.Hibernate简介
官网:http://hibernate.org/
Hibernate是一个开放源代码的对象关系映射框架,(Object Relation Mapping,简称ORM)框架,同时也是Java领域的一个持久化框架。它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
持久化
狭义的理解,持久化仅指将对象永久保存在数据库中;而广义的理解则包括与数据库相关的增删改查和加载操作。
其中,加载是指根据特定的OID将一个对象从数据库加载到内存中。为了在系统中能够找到所需对象,需要为每一个对象分配唯一的标识号;在关系数据库中称之为主键,而在对象术语中,则叫做对象标识(Object identifier,简称OID)。
ORM框架
ORM框架的功能是实现对象与关系的映射,其思想是将关系数据库中表的记录映射为对象,程序员可以把对数据库的操作直接转化为对对象的操作。其中,具体映射关系:
|面向对象概念 |面向关系概念 |
|类|表|
| 对象 |表的行 |
| 属性 |表的 列 |
ORM框架采用元数据(描述数据的数据)来描述对象关系映射细节,元数据通常采用XML格式存放在专门的对象关系映射文件中。
业务逻辑层域模型(对象/属性/关联/继承/多态)持久化层 数据库层关系数据模型(表/字段/索引/主键/外键)
对象-关系映射文件(XML)
就目前而言,较为流行的ORM框架主要有Hibernate、myBatis、TopLink、OJB等。其中,Hibernate框架较为成熟、可完成对象的持久化操作、允许开发者采用面向对象的方式来操作关系数据库、可消除针对特定数据库厂商的SQL代码;然而,myBatis框架相比Hibernate更为灵活、运行速度快,但其开发速度慢、不支持纯粹的面向对象操作、需熟悉sql语句并且熟练使用sql语句优化功能。
下载包
版本:hibernate-release-4.3.5.Final
1documentation
2lib
required
antlr-2.7.7
dom4j-1.6.1
hibernate-commons-annotation-4.0.3
hibernate-core-4.5.5.Final
hibernate-jpa-2.1-api-1.0.0.Final
jandex-1…1.0.Final
javassist-3.18.1-GA
jboss-logging-3.1.3.GA
jboss-logging-annotations-1.2.0.Beta1
jboss-transaction-api_1.2_spec-1.0.0
3project
核心API
Hibernate的API一共有6个,分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。通过这些接口,可以对持久化对象进行存取、事务控制。
Session
Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSession对象称为用户session。
SessionFactory
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
Transaction
Transaction 接口是一个可选的API,可以选择不使用这个接口,取而代之的是Hibernate 的设计者自己写的底层事务处理代码。 Transaction 接口是对实际事务实现的一个抽象,这些实现包括JDBC的事务、JTA 中的UserTransaction、甚至可以是CORBA 事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移植。
Query
Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。
Criteria
Criteria接口与Query接口非常类似,允许创建并执行面向对象的标准化查询。值得注意的是Criteria接口也是轻量级的,它不能在Session之外使用。
Configuration
Configuration 类的作用是对Hibernate 进行配置,以及对它进行启动。在Hibernate 的启动过程中,Configuration 类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。虽然Configuration 类在整个Hibernate 项目中只扮演着一个很小的角色,但它是启动hibernate 时所遇到的第一个对象。
实现步骤
工作原理
hibernate工作原理:
1、通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
2、由hibernate.cfg.xml中的<mappingresource=“com/xx/xx.hbm.xml”/>读取解析映射信息。
3、通过config.buildSessionFactory();//得到sessionFactory。
4、sessionFactory.openSession();//得到session。
5、session.beginTransaction();//开启事务。
6、persistent operate;
7、session.getTransaction().commit();//提交事务
8、关闭session;
9、关闭sessionFactory;
hibernate优点:
1、封装了jdbc,简化了很多重复性代码。
2、简化了DAO层编码工作,使开发更对象化了。
3、移植性好,支持各种数据库,如果换个数据库只要在配置文件中变换配置就可以了,不用改变hibernate代码。
4、支持透明持久化,因为hibernate操作的是纯粹的(pojo)java类,没有实现任何接口,没有侵入性。所以说它是一个轻量级框架。
缓存机制
一级缓存:session级的缓存也叫事务级的缓存,只缓存实体,生命周期和session一致。不能对其进行管理。
不用显示的调用。
二级缓存:sessionFactory缓存,也叫进程级的缓存,使用第3方插件实现的,也只缓存实体,生命周期和sessionFactory一致,可以进行管理。
首先配置第3方插件,我们用的是EHCache,在hibernate.cfg.xml文件中加入
<propertyname=“hibernate.cache.user_second_level_cache”>true
在映射中也要显示的调用,<cacheusage=“read-only”/>
二级缓存之查询缓存:对普通属性进行缓存。如果关联的表发生了修改,那么查询缓存的生命周期也结束了。
在程序中必须手动启用查询缓存:query.setCacheable(true);
2.Hibernate4版Hello World实现
新建项目Hibernate01
->新建包hibernate4。引入必需的包lib-required里面所有的包。
required
antlr-2.7.7
dom4j-1.6.1
hibernate-commons-annotation-4.0.3
hibernate-core-4.5.5.Final
hibernate-jpa-2.1-api-1.0.0.Final
jandex-1…1.0.Final
javassist-3.18.1-GA
jboss-logging-3.1.3.GA
jboss-logging-annotations-1.2.0.Beta1
jboss-transaction-api_1.2_spec-1.0.0
->新建包mysql。添加连接数据库的驱动包,mysql-connector-java-3.1.12-bin.jar。
->配置文件,配置整个项目,连接数据库
Hibernate从其配置文件中读取和数据库连接的有关信息,该文件应位于应用的classpath下。
新建hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--1.数据库连接设置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- 方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 控制台显示SQL -->
<property name="show_sql">true</property>
<!--执行操作时是否是否对SQL进行格式化 -->
<property name="format_sql">true</property>
<!-- 自动更新表结构 -->
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
->创建持久化Java类。model层,新建对象Student.java
package com.java1234.model;
public class Student {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
->创建对象关系映射文件。这个对象的映射文件Student.hbm.xml
Hibernate采用XML格式文件来指定对象和关系数据之间的映射,其在运行时 将根据该映射文件来生成所对应的SQL语句。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
//name是类里面的字段column是表里面的
//id是主键,其他的字段用property。指定主键的生成方式 native:使用数据库本地的方式
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
->hibernate.cfg.xml中加一句
<mapping resource="com/java1234/model/Student.hbm.xml"/>
如果发现有Student类,表里面却没有对应的表。就会自动创建一个表。
->但是没能力自己建数据库,所以需要自己先建一个数据库
->service层用来测试StudentTest .java
编写代码访问数据库:
1Configuration类
作用:负责管理Hibernate的配置信息,主要包括Hibernate运行的底层信息(如数据库的URL、用户名、密码、JDBC驱动类、数据库连接池、数据库Dialect等,对应hibernate.cfg.xml文件)和持久化类与数据表的映射关系(对应*.hbm.xml 文件)。
创建Configuration对象:
若采用hibernate.properties文件配置,则Configuration cfg = new Configuration();
若采用hibernate.cfg.xml文件配置,则Configuration cfg = new Configuration().configure(),且Configuration对象的configure()方法还支持带参数的访问。
2SessionFactory接口
针对单个数据库映射关系经过编译后的内存镜像,是线程安全的;
构造SessionFactory很耗资源,一般情况下一个应用中只初始化一个SessionFactory对象;
SessionFactory是生成Session的工厂,该对象一旦构造完毕,即被赋予特定的配置信息;
Hibernate4新增ServiceRegistry接口,所有基于Hibernate的配置或服务都必须统一向这个ServiceRegistry注册后才能生效。
3Session接口
Session对象是应用程序与数据库之间交互操作的一个单线程对象,是Hibernate的运作中心,所有持久化对象必须在Session的管理下才可以进行持久化操作;另外,此对象生命周期很短,且具有一个一级缓存,显式执行flush()方法前,所有的持久层操作的数据都缓存在Session对象处,其相当于JDBC中的Connection。
注意:持久化类与Session关联起来后就具有了持久化的能力。其中,Session接口的常用方法主要有:
开启事务:beginTransaction();
取得持久化对象的方法:get()、load();
持久化对象的操作:save()、update()、saveOrUpdate()、delete();
管理Session对象:isOpen()、flush()、clear()、evict()、close()等。
4Transaction(事务)
其代表一次原子操作,具有数据库事务的概念,即所有持久层操作都应该在事务管理下进行,包括只读操作。其常用方法主要有:
rollback():撤销事务操作;
wasCommitted():检查事务是否提交;
commit():提交事务,即提交相关联的session实例。
package com.java1234.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import com.java1234.model.Student;
public class StudentTest {
public static void main(String[] args) {
// 1). 创建Configuration对象:对应Hibernate基本配置信息和对象关系映射信息
Configuration configuration=new Configuration().configure();
// 2). 创建一个ServiceRegistry对象:Hibernate4.x新增Hibernate的任何配置和服务都需要在该对象中注册后才能有效
ServiceRegistry serviceRegistry=new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
//3). 创建SessionFactory对象, 获取Session工厂,为了生成session
SessionFactory sessionFactory=configuration.buildSessionFactory(serviceRegistry);
//4).生成一个session,session是一个带缓存的数据库连接
Session session=sessionFactory.openSession();
//5).开启事务
session.beginTransaction();
//6).执行保存操作
Student s=new Student();
s.setName("张三");
session.save(s);
//7).提交事务
session.getTransaction().commit(); // 提交事务
//8)关闭session对象
session.close(); // 关闭session,缓存是占内存空间的,所以要关掉
//9)关闭sessionFactory对象
sessionFactory.close(); // 关闭session工厂
}
}
->启动程序:打开服务器。发现在我们创建的数据库Tables中自动生成了一个表t_student。
字段有styID和name。多了一条记录"张三",因为geneeator为native,自增,id为1。
控制台上打印出"Hibernate:insert into t_student (name) values_(?)"。
3.Hibernate配置文件hibernate.hbm.xml
Hibernate配置文件主要用于配置数据库连接和Hibernate运行时所需的各种属性,每个Hibernate配置文件对应一个Configuration对象;其中,Hibernate配置文件可以有hibernate.properties和hibernate.cfg.xml两种形式,常用hibernate.cfg.xml。
-
配置数据库连接属性
hibernate.connection.url:数据库URL;
hibernate.connection.username:数据库用户名;
hibernate.connection.password:数据库用户密码;
hibernate.connection.driver_class:数据库JDBC驱动;
hibernate.dialect:配置数据库方言,即根据底层数据库的不同,产生不同的SQL语句,Hibernate 会针对数据库的特性在访问时进行优化。 -
配置C3P0数据库连接池属性
配置C3P0数据库连接池之前,需要加入所依赖的jar包,其位于hibernate-release-4.2.4.Final\lib\optional\c3p0目录下。
hibernate.c3p0.max_size:数据库连接池的最大连接数;
hibernate.c3p0.min_size:数据库连接池的最小连接数;
hibernate.c3p0.acquire_increment:当数据库连接池中连接耗尽时, 同一时刻获取多少个数据库连接;
hibernate.c3p0.max_statements:缓存Statement对象的数量;
hibernate.c3p0.timeout: 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁;
hibernate.c3p0.idle_test_period:表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时。连接池本身不会将自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来完成,该线程将连接对象最后一次被使用时间与当前时间的时间差和timeout做对比,进而决定是否销毁该连接对象。
-
配置Hibernate的基本信息
show_sql:执行操作时是否在控制台打印SQL语句,取值 true | false;
format_sql:执行操作时是否对SQL语句进行格式化,取值 true | false;
hbm2ddl.auto:在启动和停止时自动地创建、更新或删除数据库模式,取值 create | update | create-drop | validate;
hibernate.jdbc.fetch_size:设定JDBC的Statement读取数据时每次从数据库中取出的记录条数;
hibernate.jdbc.batch_size:设定对数据库进行批量删除、批量更新或批量插入时的批次大小。
4.对象关系映射文件XXX.hbm.xml
Hibernate根据对象关系映射文件来理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表字段之间的对应关系,并在运行时生成各种SQL语句。
1映射文件根元素
2class元素
3. 映射对象标识符(OID)
作用:与数据表主键对应,用于建立内存中的对象和数据库表中记录的对应关系,进而通过标识符生成器来为主键赋值。
**主键:**Hibernate推荐在数据表中使用代理主键,即不具备业务含义的字段;代理主键通常为整数类型,因其比字符串类型要节省更多的存储空间。
设置:在对象关系映射文件中,id元素用来设置对象标识符,generator子元素用来设置标识符生成器。
Hibernate提供了标识符生成器接口IdentifierGenerator,并提供了各种内置实现。
4. 标识符生成器
Hibernate提供的内置标识符生成器有:
4.1 increment标识符生成器
作用:由Hibernate以递增的方式为代理主键赋值。
**原理:**Hibernate先读取数据库表中主键的最大值(有并发问题),执行保存操作时即在最大值基础上递增1。
适用范围:
1). 数据库系统:不依赖于底层数据库系统,适用于所有数据库系统;
2). 单进程访问:适用只有单个Hibernate应用进程访问同一个数据库的场合,在集群环境下不推荐使用;
3). OID定义类型:必须为long、int或short类型,若定义为byte类型,在运行时会抛出异常。
4.2 identity标识符生成器
原理:由底层数据库来负责生成标识符, 要求底层数据库把主键定义为自动增长字段类型。
适用范围:
1). 数据库系统:支持自动增长字段类型的数据库,如DB2、MySQL、MSSQLServer、Sybase等;
2). OID定义类型:必须为long、int或short类型,若定义为byte类型,在运行时会抛出异常。
4.3 sequence标识符生成器
作用:利用底层数据库提供的序列来生成标识符。
原理: Hibernate在持久化对象时,先从所指定的底层数据库序列中获得唯一的标识号,在将其作为主键值。
适用范围:
1). 数据库系统:支持序列的底层数据库,如DB2、Oracle等;
2). OID定义类型:必须为long、int或short类型,若定义为byte类型,在运行时会抛出异常。
<!-- 利用底层数据库提供的 news_seq 序列来生成标识符 -->
<id name="id">
<generator class="sequence">
<param name="sequence">news_seq</param>
</generator>
</id>
4.4 hilo标识符生成器
作用:由Hibernate按照一种high/low算法生成标识符,其从数据库中指定表的特定字段中获取high值。
原理: Hibernate在持久化对象时,由Hibernate负责生成主键值。
适用范围:
1). 数据库系统:支持所有的数据库,因其不依赖于底层数据库系统;
2). OID定义类型:必须为long、int或short类型,若定义为byte类型,在运行时会抛出异常。
<!-- hilo标识符生成器在生成标识符时,需要读取并修改HI_TABLE表中的NEXT_VALUE值 -->
<id name="id">
<generator class="hilo">
<param name="table">HI_TABLE</param>
<param name="colume">NEXT_VALUE</param>
<param name="max_lo">10</param>
</generator>
</id>
4.5 native标识符生成器
原理:依据底层数据库对自动生成标识符的支持能力,选择使用identity、sequence或hilo标识符生成器。
适用范围:
1). 数据库系统:支持所有的数据库,适合于跨数据库平台开发;
2). OID定义类型:必须为long、int或short类型,若定义为byte类型,在运行时会抛出异常。
5. property元素
6. Hibernate内置映射类型
6.1 映射类型对应关系
6.2 Java时间日期类型的Hibernate映射
对应关系:在JDBC API中,java.util.Date类是java.sql.Date、java.sql.Time和java.sql.Timestamp类的父类,分别与标准SQL类型中的DATE(日期)、TIME(时间)和TIMESTAMP(时间戳,同时包含日期和时间)类型对应。
具体使用:设置持久化类的Date类型时,应设置为java.util.Date,且可通过property的type属性来将其映射为DATE、TIME或TIMESTAMP类型。
<!-- 强制将java.util.Date类映射为SQL类型中的TIMESTAMP类 -->
<property name="date" type="timestamp">
<column name="DATE" />
</property>
-
Java大对象类型的Hiberante映射
@Test
public void testBlob() throws Exception {
News news = new News();
news.setAuthor(“qiaob”);
news.setTitle(“qiaobc”);
news.setContent(“I Love You!”); // 大文本
news.setDate(new Date());// 保存大对象: private Blob image; 二进制图片 InputStream in = new FileInputStream("test.png"); Blob image = Hibernate.getLobCreator(session).createBlob(in, in.available()); news.setImage(image); session.save(news); // 输出大对象 news = (News) session.get(News.class, 14); Blob image2 = news.getImage(); InputStream inputStream = image2.getBinaryStream(); System.out.println(inputStream.available());
}
-
映射组成关系
二.Hibernate4CRUD 体验
1.HibernateUtil 封装
新建项目Hibernate02
->新建包util,新建HibernateUtil.java。将上一节中service中的操作映射的内容封装起来。
package com.java1234.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
private static final SessionFactory sessionFactory=buildSessionFactory();
private static SessionFactory buildSessionFactory(){
Configuration configuration=new Configuration().configure();
// 实例化配置文件
ServiceRegistry serviceRegistry=new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
// 实例化服务登记
return configuration.buildSessionFactory(serviceRegistry);
// 获取Session工厂
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
}
->service层StudentTest.java
package com.java1234.service;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s=new Student();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
sessionFactory.close();
}
}
->启动程序:打开服务器,结果还是和和上一个例子的一样。
2.XML 版 CRUD 实现
->StudentTest.java
package com.java1234.service;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private void add(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s=new Student();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void delete(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
//参数写上类和主键
Student student=(Student)session.get(Student.class, Long.valueOf(1));
session.delete(student);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void update(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student student=(Student)session.get(Student.class, Long.valueOf(2));
student.setName("张三2");
session.save(student);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void getAllStudent(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
String hql="from Student";
Query query=session.createQuery(hql);
List<Student> studentList=query.list();
for(Student student:studentList){
System.out.println(student);
}
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
public static void main(String[] args) {
StudentTest studentTest=new StudentTest();
//studentTest.add();
//studentTest.delete();
//studentTest.update();
//studentTest.getAllStudent();
}
}
->启动程序:打开服务器
删除了id为1的记录,控制台上打印出
“Hibernate:select student0_.stuId as stuId1_0_0_,student0_.name as name2_0_0_”
“Hibernate:delete from t_student where stuId=?”
->启动程序
更新了id为2的记录,控制台上打印出
“Hibernate:select student0_.stuId as stuId1_0_0_,student0_.name as name2_0_0_”
“Hibernate:update t_student set name=? where stuId=?”
->启动程序
查询到了所有的记录,控制台上打印出
“Hibernate:select student0_.stuId as stuId1_0_0_,student0_.name as name2_0_0_”
“Student[id=2,name=张三2]”
“Student[id=3name=张三]”
3.注解版 CRUD 实现
->model层新建Teacher.java,用了注解之后,就不需要再使用Teacher.hbm.xml了
package com.java1234.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
//代表它是一个映射的实体
@Table(name="t_teacher")
//映射到这个表中
public class Teacher {
private long id;
private String name;
@Id
//主键
@GeneratedValue(generator="_native")
//生成策略
@GenericGenerator(name="_native",strategy="native")
//定义策略
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + "]";
}
}
->hibertnate.cfg.xml中加上一句
<mapping class="com.java1234.model.Teacher"/>
->启动服务器,数据库中多了一个表t_teacher,字段为id和name。
->新建TeacherTest .java
package com.java1234.service;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Teacher;
import com.java1234.util.HibernateUtil;
public class TeacherTest {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private void add(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher s=new Teacher();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void delete(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher Teacher=(Teacher)session.get(Teacher.class, Long.valueOf(1));
session.delete(Teacher);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void update(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher Teacher=(Teacher)session.get(Teacher.class, Long.valueOf(2));
Teacher.setName("张三2");
session.save(Teacher);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
public void getAllTeacher(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
String hql="from Teacher";
Query query=session.createQuery(hql);
List<Teacher> TeacherList=query.list();
for(Teacher Teacher:TeacherList){
System.out.println(Teacher);
}
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
public static void main(String[] args) {
TeacherTest TeacherTest=new TeacherTest();
// TeacherTest.add();
// TeacherTest.delete();
// TeacherTest.update();
TeacherTest.getAllTeacher();
}
}
->启动程序,都能生效
4.Junit4 单元测试框架引入
->新建包junit4,把包junit4.4.jar拷贝进来
->把TeacherTest .java增删改查方法的private改为public
package com.java1234.service;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Teacher;
import com.java1234.util.HibernateUtil;
public class TeacherTest {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private void add(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher s=new Teacher();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void delete(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher Teacher=(Teacher)session.get(Teacher.class, Long.valueOf(1));
session.delete(Teacher);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
private void update(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher Teacher=(Teacher)session.get(Teacher.class, Long.valueOf(2));
Teacher.setName("张三2");
session.save(Teacher);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
public void getAllTeacher(){
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
String hql="from Teacher";
Query query=session.createQuery(hql);
List<Teacher> TeacherList=query.list();
for(Teacher Teacher:TeacherList){
System.out.println(Teacher);
}
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
public static void main(String[] args) {
TeacherTest TeacherTest=new TeacherTest();
// TeacherTest.add();
// TeacherTest.delete();
// TeacherTest.update();
TeacherTest.getAllTeacher();
}
}
->在上面这里类中open–JUnit Text Case
自动生成一个类,我们取名叫做TeacherTest2.java
然后选择你要进行单元测试的几个方法
生成了TestXXX方法,里面的内容我们自己添加
package com.java1234.service;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Test;
import com.java1234.model.Teacher;
import com.java1234.util.HibernateUtil;
public class TeacherTest2 {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
@Test
public void testAdd() {
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher s=new Teacher();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testDelete() {
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher Teacher=(Teacher)session.get(Teacher.class, Long.valueOf(2));
session.delete(Teacher);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testUpdate() {
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Teacher Teacher=(Teacher)session.get(Teacher.class, Long.valueOf(3));
Teacher.setName("张三2");
session.save(Teacher);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testGetAllTeacher() {
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
String hql="from Teacher";
Query query=session.createQuery(hql);
List<Teacher> TeacherList=query.list();
for(Teacher Teacher:TeacherList){
System.out.println(Teacher);
}
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
}
->在此类的左边单独run某一个TestXXX方法即可,这样就不需要每次都去改main方法了
三.映射对象标识符(OID)
1.Hibernate 用对象标识符(OID)来区分对象
新建项目Hibernate03
->新建mdoel层的类Student.java
->新建service层StudentTest.java
package com.java1234.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest {
public static void main(String[] args) {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s1=(Student)session.get(Student.class, Long.valueOf(1));
Student s2=(Student)session.get(Student.class, Long.valueOf(2));
Student s3=(Student)session.get(Student.class, Long.valueOf(1));
System.out.println(s1==s2);
System.out.println(s1==s3);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
}
->启动服务器,控制台上打印出"false"“true”
这说明
2.Hibernate 对象标识符生成策略
即生成主键的策略
主键的分类 业务主键 VS 代理主键 代理主键是不具有业务性的;
1,increment 由 Hibernate 自动以递增的方式生成标识符,适用代理主键;
2,identity 由底层数据库生成标识符;适用代理主键;
3,sequcence 由 Hibernate 根据底层数据库的序列来生成标识符;适用代理主键;
4,hilo Hibernate 根据 high/low 算法来生成标识符。适用代理主键
5,native 根据底层数据库对自动生成标识符的支持能力, 来选择 identity,sequence 或 hilo;适用代理主键;
increment
新建StudentTest2.java
package com.java1234.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest2 {
public static void main(String[] args) {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s=new Student();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
}
->Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="increment"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
->启动程序,控制台上打印出
“Hibernate:select max(stuId) from t_student”
“Hibernate:insert into t_student (name,stuId) values (?,?)”
identity
->Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="identity"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
->启动程序,控制台上打印出
“Hibernate:insert into t_student (name) values (?)”
sequcence
->Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="sequcence "></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
->启动程序,控制台上打印出
hilo
->Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="hilo"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
->启动程序,控制台上打印出
native
->Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
->启动程序,控制台上打印出
“Hibernate:insert into t_student (name) values (?)”
四.关联关系多对一映射
1.学生班级多对一映射实现(单向)
概念:单向多对一只需要从多的一端可以访问到一的一端即可。
单向就是学生端关联班级,但班级端不关联学生。
班级是1 学生是多。
只可以从学生的一端访问到班级的一端。
Class Student
id id
name name
c
域模型:从学生到班级的多对一单向关联,需要在学生类中定义一个班级属性。而在班级类中无需定义存放学生对象的集合属性。
t_class t_student
classId stuId
className stuName
classId
关系数据模型:学生表中的classId参照班级表的主键。
显然,无法直接使用property元素来映射Student类的class属性,Hibernate提供many-to-one元素来映射多对一关联关系。
<!--
映射单向n-1映射关系:stuId为Student表中的外键
实现:使用<many-to-one>元素来映射关联关系,其具体属性说明如下:
name: 设定待映射的持久化类的属性的名字
class:设定待映射的持久化类的属性的类型
column: 设定和持久化类的属性对应的表的外键
-->
<many-to-one name="c" column="classId" class="com.java1234.model.Class"></many-to-one>
新建项目Hibernate04
->新建Class.java
package com.java1234.model;
public class Class {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
->新建Class.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
</class>
</hibernate-mapping>
->新建Student.java
package com.java1234.model;
public class Student {
private long id;
private String name;
**private Class c;
//实现单项关联,要在多的一方的属性里填上一个一的对象**,这样就能从学生端获取到班级端的信息
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
**public Class getC() {
return c;
}
public void setC(Class c) {
this.c = c;
}**
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
->新建Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
**//配置多对一,要在多的这一方使用many-to-one,加上外键
<many-to-one name="c" column="classId" class="com.java1234.model.Class"></many-to-one>**
</class>
</hibernate-mapping>
->hibernate.cfg.xml
<mapping resource="com/java1234/model/Student.hbm.xml"/>
<mapping resource="com/java1234/model/Class.hbm.xml"/>
->新建service,StudentTest .java
package com.java1234.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.java1234.model.Class;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest {
public static void main(String[] args) {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
/以下是保存一个班级和两个学生
Class c=new Class();
c.setName("08计本");
session.save(c);
Student s1=new Student();
s1.setName("张三");
s1.setC(c);//设定关联关系,从学生端多的一端设定到班级少的一端
Student s2=new Student();
s2.setName("李四");
s2.setC(c);//设定关联关系,从学生端多的一端设定到班级少的一端
session.save(s1);//注意这里保存,如果像这样先1后n,只有三条insert语句
session.save(s2);//如果先n后1,有3条insert语句1条更新语句
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
}
->启动程序:
控制台上打印出
“Hibernate:insert into t_class(className) values(?)”
“Hibernate:insert into t_class(stuName,classId) values(?,?)”
“Hibernate:insert into t_class(stuName,classId) values(?,?)”
创建了新的表,并且信息插入成功。
->上一次的jniit中每次都要写session这些,下面我们简化一下junit操作
2.Junit4 方法详解
setUpBeforeClass() 类初始化前调用;
tearDownAfterClass() 类初始化后调用;
setUp() 在测试方法前调用;
tearDown() 在测试方法后调用;
新建StudentTest2.java
package com.java1234.service;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class StudentTest2 {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
System.out.println("类初始化前调用...");
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
System.out.println("类初始化后调用...");
}
@Before
public void setUp() throws Exception {
System.out.println("在测试方法前调用...");
}
@After
public void tearDown() throws Exception {
System.out.println("在测试方法后调用...");
}
@Test
public void test() {
System.out.println("测试方法");
}
}
->启动程序:控制台上打印出
“类初始化前调用…”
“在测试方法前调用…”
“测试方法”
“在测试方法后调用…”
“类初始化后调用…”
以下是测试的运用
->StudentTest3 .java
package com.java1234.service;
import static org.junit.Assert.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.java1234.model.Class;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest3 {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testSaveClassAndStudent() {
Class c=new Class();
c.setName("08计本");
session.save(c);
Student s1=new Student();
s1.setName("张三");
s1.setC(c);
Student s2=new Student();
s2.setName("李四");
s2.setC(c);
session.save(s1);
session.save(s2);
}
}
->启动程序:控制台上打印出
“Hibernate:insert into t_class(className) values(?)”
“Hibernate:insert into t_class(stuName,classId) values(?,?)”
“Hibernate:insert into t_class(stuName,classId) values(?,?)”
3.级联保存更新
在这端,cascade 默认是”none”,假如我们希望在持久化多的一端的时候,自动级联保存和更新一的一端,我们可以把 cascade 设置成”save-update”;
->StudentTest3 .java里改为下面这个方法实现级联
@Test
public void testSaveClassAndStudentWithCascade() {
Class c=new Class();
c.setName(“08计本”);//没有用session保存,在多的一端自动级联保存一的一端
Student s1=new Student();
s1.setName("张三");
s1.setC(c);
Student s2=new Student();
s2.setName("李四");
s2.setC(c);
session.save(s1);
session.save(s2);
}
->Student.hbm.xml要在配置文件中加上级联的属性
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
**<many-to-one name="c" column="classId" class="com.java1234.model.Class" cascade="save-update"></many-to-one>**
</class>
</hibernate-mapping>
->以上实现了级联保存
多的一端有持久,一的一段会自动级联保存
先添加三个学生表,再更新一个班级表
4.测试manytooneget
以上测试了manytoonesave,下面测试manytooneget/update/delete
@Test
public void testManyToOneGet() {
// 1. 查询n的一端的对象时,默认不会查询与其关联的1的一端的对象
Student stu = (Student) session.get(Student.class, 1);
System.out.println(stu.getStudentName());
// 4. 仅获取n的一端的对象时,默认情况下其关联的1的一端的对象是一个代理对象
// com.qiaobc.hibernate.entities.n21.Customer_$$_javassist_3
System.out.println(stu.getClass().getClass().getName()); // 不会查询关联对象
// 3. 在查询关联对象时,若session关闭,则默认情况下会抛出org.hibernate.LazyInitializationException异常
// session.close();
// 2. 若需要使用关联对象时,才发送SELECT语句
System.out.println(stu);
}
@Test
public void testManyToOneUpdate() {
// 2条SELECT语句,1条UPDATE语句
Student stu = (Student) session.get(Student.class, 1);
stu.getClass().setClassName("qiaob");
}
@Test
public void testManyToOneDelete() {
// 在未设定级联关系的情况下,1的一端的对象有n的一端对象在引用,故其不能删除
Class class= (Class) session.get(Class.class, 1);
session.delete(class);
}
5.班级学生一对多映射实现(双向)
概念:双向1-n与双向 n-1是完全相同的两种情形,即需要在1的一端可以访问n的一端,反之依然。
在前面,我们讲了在学生端获取班级信息,如果我们要在班级端获取所有学生的信息,就需要使用双向关联。
Class Student
id id
name name
students class
域模型:从学生到班级的多对一双向关联需要在学生类中定义一个班级属性,而在班级类中需定义存放学生对象的集合属性。
t_class t_student
classId stuId
className stuName
students classId
关系数据模型:学生表中的classId参照班级表的主键。
在双向1-n关联关系中,既需要在1的一端配置一对多映射关系(set元素),又需要在n的一端配置多对一映射关系(many-to-one元素)。示例如下:
<!-- 1. 在Student.hbm.xml中:配置n-1映射关系 -->
<many-to-one name="c" class="Class" column="classId"></many-to-one>
<!-- 2. 在Class.hbm.xml中:配置1-n映射关系 -->
<set name="students" cascade="save-update">
<key column="classId"></key>//这里的主键key对应的别的表的外键,要关联一下
<one-to-many class="com.java1234.model.Student"/>
</set>
新建项目Hibernate04-02
->新建Class.java
package com.java1234.model;
import java.util.HashSet;
import java.util.Set;
public class Class {
private long id;
private String name;
**private Set<Student> students=new HashSet<Student>();
//在一的这一段加上学生的集合这个属性,set集合能确保里面的对象是唯一的,这样就能在班级端获取所有学生端的信息**
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
->Class.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
**//在一的这一端进行设置,这里注意要级联哟
<set name="students" cascade="save-update">
<key column="classId"></key>
//key对应的外键,要关联一下
<one-to-many class="com.java1234.model.Student"/>
</set>**
</class>
</hibernate-mapping>
->Student.java
package com.java1234.model;
public class Student {
private long id;
private String name;
**private Class c;**
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
**public Class getC() {
return c;
}
public void setC(Class c) {
this.c = c;
}**
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
->Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
**<many-to-one name="c" column="classId" class="com.java1234.model.Class" cascade="save-update"></many-to-one>**
</class>
</hibernate-mapping>
->service里新建StudentTest.java
package com.java1234.service;
import java.util.Iterator;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.java1234.model.Class;
import com.java1234.model.Student;
import com.java1234.util.HibernateUtil;
public class StudentTest {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testSaveClassAndStudent() {
Class c=new Class();
c.setName("08计本");
Student s1=new Student();
s1.setName("张三");
Student s2=new Student();
s2.setName("李四");
**
//通过另外一种方式来保存学生
c.getStudents().add(s1);
c.getStudents().add(s2);
session.save(c);// 1. 先1后n:3条INSERT语句,2条UPDATE语句,因为均需要维护关联关系
// 注意:可以设置1的一端的set属性inverse="true",使其放弃维护关联关系
// 2. 先n后1:3条INSERT语句,4条UPDATE语句,因为均需要维护关联关系
//从一的一端去保存多的一端**
}
}
->启动程序:控制台上打印出:
“Hibernate:insert into t_class(className) values(?)”
“Hibernate:insert into t_class(stuName,classId) values(?,?)”
“Hibernate:insert into t_class(stuName,classId) values(?,?)”
“Hibernate:update t_student set classId=? where stuId=?”
“Hibernate:update t_student set classId=? where stuId=?”
先添加三个班级表,再更新两个学生表
->StudentTest.java中加上方法,通过班级端来查找学生
@Test
public void getStudentsByClass(){
Class c=(Class)session.get(Class.class, Long.valueOf(2));//从一的一端获取多的一端的数据
Set<Student> students=c.getStudents();
Iterator it=students.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
->启动程序:控制台上打印出:
“Hibernate:select class0_.classId as classId1_0_0_,class0_.classNmae”
“Hibernate:select students0_.classId as classId3_0_0_,student0_.studentNmae”
“Student[id=1,name=李四]”
“Student[id=2,name=张三]”
实现双向多对一的方式的优点:既可以从一的一端获取多的一端的数据,也可以从多的一端获取一的一端的数据。
以下是get/update/delete的实例
@Test
public void testManyToOneGet() {
// 1. 查询n的一端的对象时,默认不会查询与其关联的1的一端的对象
Student stu = (Student) session.get(Student.class, 1);
System.out.println(stu.getStuName());
// 4. 仅获取n的一端的对象时,默认情况下其关联的1的一端的对象是一个代理对象
// com.qiaobc.hibernate.entities.n21.Customer_$$_javassist_3
System.out.println(stu.getClass().getClass().getName()); // 不会查询关联对象
// 3. 在查询关联对象时,若session关闭,则默认情况下会抛出org.hibernate.LazyInitializationException异常
// session.close();
// 2. 若需要使用关联对象时,才发送SELECT语句
System.out.println(stu);
}
@Test
public void testOneToManyGet() {
// 1. 对n的一端的集合延迟加载
Class c= (Class) session.get(Class.class, 1);
System.out.println(c.getClassName());
// 2. 需要在持久化类中定义集合类型为Java接口类型
// org.hibernate.collection.internal.PersistentSet:
// Hibernate内置的集合类型,具有延迟加载和存放代理对象的功能
System.out.println(c.getStudents().getClass().getName());
// 3. 可能会抛出org.hibernate.LazyInitializationException异常
// session.close();
// System.out.println(c.getStudents().size());
// 4. 在需要使用集合中元素的时候才进行初始化。
}
@Test
public void testManyToOneUpdate() {
// 2条SELECT语句,1条UPDATE语句
Student stu = (Student ) session.get(Student .class, 1);
stu.getClass().setClassName("qiaob");
}
@Test
public void testManyToOneUpdate2() {
Class c= (Class ) session.get(Class .class, 1);
c.getStudents().iterator().next().setStuName("qiaobei");
}
@Test
public void testManyToOneDelete() {
// 在未设定级联关系的情况下,1的一端的对象有n的一端对象在引用,故其不能删除
Class c= (Class ) session.get(Class .class, 1);
c.delete(c);
}
6.inverse 属性
StudentTest.java中增加一个添加方法
@Test//这个注解必须自己添上
public void testAdd(){
Class c=new Class();
c.setName("09计本");
Student s1=new Student();
s1.setName("王五");
session.save(c);
session.save(s1);
}
@Test
public void testInverse(){
//新建两个持久化对象
Class c=(Class)session.get(Class.class, Long.valueOf(1));
Student s=(Student)session.get(Student.class, Long.valueOf(1));
s.setC(c);//学生设置班级
c.getStudents().add(s);//班级设置学生
}
->执行结果:
“Hibernate:select class0_classId as classId1_0_0_,class0_.className”
“Hibernate:select student0_.stuId as stuId1_1_0,student0_.stuName a”
“Hibernate:select students0_.classId as classId3_0_0,students0_.stu”
“Hibernate:update t_student set stuName=? classId=? where stuId=?”
“Hibernate:update t_student set classId=? where stuId=?”
第一条更新是根据s.setC©;//学生设置班级
第二条更新记录是根据c.getStudents().add(s);//班级设置学生
其实只需要一条就够了,两条是冗余的
inverse是在one-many一端
->Class.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
**//在一的这一端进行设置,这里注意要级联哟
<set name="students" cascade="save-update" **inverse="true"**>
<key column="classId"></key>
//key对应的外键,要关联一下
<one-to-many class="com.java1234.model.Student"/>
</set>**
</class>
</hibernate-mapping>
相当于在一的一端来维护主外键关系
->执行结果:
->执行结果:
“Hibernate:select class0_classId as classId1_0_0_,class0_.className”
“Hibernate:select student0_.stuId as stuId1_1_0,student0_.stuName a”
“Hibernate:select students0_.classId as classId3_0_0,students0_.stu”
“Hibernate:update t_student set stuName=? classId=? where stuId=?”
更新是根据s.setC©;//学生设置班级
第二条直接给忽略掉了
一般就是多的一端设置一的一端
reverse的作用是用多的一端来维护
7.级联删除
比如:删除一个班级时,把这个班里面的所有学生都给删掉
StudentTest.java中增加一个添加方法
@Test
public void testDeleteClassCascade(){
Class c=(Class)session.get(Class.class, Long.valueOf(1));
session.delete(c);
}
->执行结果
默认是不能删除的,因为你删除的这个表的主键,被别的表当做外键关联
要设置cascade="delete"才行
->Class.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
**//在一的这一端进行设置,这里注意要级联哟
<set name="students" **cascade="delete"** inverse="true" >
<key column="classId"></key>
//key对应的外键,要关联一下
<one-to-many class="com.java1234.model.Student"/>
</set>**
</class>
</hibernate-mapping>
->执行结果:删除掉了
“Hibernate:select class0_classId as classId1_0_0_,class0_.className as”
“Hibernate:select students0_.classId as classId3_0_0,students0_.stu”
“Hibernate:delete from t_student wherre stuId=?”
“Hibernate:delete from t_class wherre classId=?”
级联删除是一个危险的操作
8.set元素的属性总结
1 inverse属性
Hibernate中通过inverse属性来决定是由双向关联的哪一方来维护表与表之间的级联关系,默认情况下父子双方均会维护父子关系(保存操作时会多出UPDATE语句)。
实现:通过设置inverse=true使当前方为被动方,即不维护级联关系。
注意:在双向1-n关系中,将n的一方设置为主动方将有助于性能改善。
2 cascade属性
了解:在对象关系映射文件中,用于映射持久化类之间关联关系的元素都有cascade属性,用于指定如何操纵与当前对象关联的其他对象.。
3 在数据库中对集合排序
order-by属性可使Hibernate在检索集合对象时,利用order by子句对集合进行排序,该属性中还可以加入SQL函数。
9.一对多双向自身关联关系映射
比如菜单中,有根目录、一级菜单、二级菜单
新建一个类Node.java
package com.java1234.model;
import java.util.HashSet;
import java.util.Set;
public class Node {
private long id;
private String name;
private Node parentNode;//当前节点与父节点是多对一的关系
private Set<Node> childNodes=new HashSet<Node>();//当前节点与它下面的节点是一对多的 关系
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node getParentNode() {
return parentNode;
}
public void setParentNode(Node parentNode) {
this.parentNode = parentNode;
}
public Set<Node> getChildNodes() {
return childNodes;
}
public void setChildNodes(Set<Node> childNodes) {
this.childNodes = childNodes;
}
}
->新建Node.hbm.xml,把两种关系都放在一起
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.java1234.model">
<class name="Node" table="t_node">
<id name="id" column="nodeId">
<generator class="native"></generator>
</id>
<property name="name" column="nodeName"></property>
**
//首先我们把自身当做多的一端,去配置多对一,即要配置一个父节点
<many-to-one name="parentNode" column="parentId" class="com.java1234.model.Node" cascade="save-update"></many-to-one>
//然后我们把自身当做一的一端,去配置一对多,即要配置一个子节点
<set name="childNodes" inverse="true">
<key column="parentId"></key>//对应的外键是paertId
<one-to-many class="com.java1234.model.Node"/>
</set>**
</class>
</hibernate-mapping>
->新建NodeTest.java
package com.java1234.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.java1234.model.Node;
import com.java1234.util.HibernateUtil;
public class NodeTest {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testSaveMenu() {
Node node=new Node();
node.setName("根节点");
Node subNode1=new Node();
subNode1.setName("子节点1");
Node subNode2=new Node();
subNode2.setName("子节点2");
subNode1.setParentNode(node);
subNode2.setParentNode(node);
session.save(subNode1);
session.save(subNode2);
}
}
->执行结果:
“Hibernate:insert into t_node (nodeName,parentId) values (?,?)”
“Hibernate:insert into t_node (nodeName,parentId) values (?,?)”
“Hibernate:insert into t_node (nodeName,parentId) values (?,?)”
生成的表t_node中,生成一个自身的
nodeId nodeName parentId
1 根节点 null
2 子节点1 1
3 子节点2 1