hibernate基本知识
使用JDBC做数据库相关功能开发会做很多重复性的工作,比如创建连接,关闭连接,把字段逐一映射到属性中。 Hibernate把这一切都封装起来了,使得数据库访问变得轻松而简单,代码也更加容易维护。
hibernate配置步骤:
1:导入jar包
2:创建对应实体类对应数据库表
3:配置对应表的xml用于映射实体类对应数据库表
4:配置hibernate.cfg.xml 配置访问数据库要用到的驱动,url,账号密码等等
其他配置及含义:
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
这表示使用MYSQL方言。 什么方言呢? 因为在代码层面,开发人员不用关心底层到底用Oracle还是Mysql,写的代码都是一样的。 可是Oracle和Mysql所用的sql语句的语法是有所区别的,那么这件事就交给Hibernate来做了。这个时候就需要告诉Hibernate底层用的是什么数据库,它才知道应该用什么样的“方言” 去对话。
<property name="current_session_context_class">thread</property>
这是Hibernate事务管理方式,即每个线程一个事务
<property name="show_sql">true</property>
这表示是否在控制台显示执行的sql语句
<property name="hbm2ddl.auto">update</property>
这表示是否会自动更新数据库的表结构,有这句话,其实是不需要创建表的,因为Hibernate会自动去创建表结构
<mapping resource="com/how2java/pojo/Product.hbm.xml" />
这表示Hibernate会去识别Product这个实体类
实体类对象在Hibernate中有3种状态
分别是瞬时,持久和脱管
瞬时 指的是没有和hibernate发生任何关系,在数据库中也没有对应的记录,一旦JVM结束,这个对象也就消失了
持久 指得是一个对象和hibernate发生联系,有对应的session,并且在数据库中有对应的一条记录
脱管 指的是一个对象虽然在数据库中有对应的一条记录,但是它所对应的session已经关闭了
new 了一个实体类;,在数据库中还没有对应的记录,这个时候实体类对象的状态是瞬时的。
通过Session的save把该对象保存在了数据库中,该对象也和Session之间产生了联系,此时状态是持久的。
最后把Session关闭了,这个对象在数据库中虽然有对应的数据,但是已经和Session失去了联系,相当于脱离了管理,状态就是脱管的
hibernate在java代码中使用的基本步骤是:
- 获取SessionFactory
- 通过SessionFactory 获取一个Session
- 在Session基础上开启一个事务
- 通过调用Session的save方法把对象保存到数据库
- 提交事务
- 关闭Session
- 关闭SessionFactory
关系数据库中表的关系在hibernate中关联
指关系数据库中表的关系在hibernate中关联
三种类型:一对多(鸟类包含喜鹊,鸟类中添加包含鸟的set集合),多对一(喜鹊属于鸟类,喜鹊类中添加鸟类属性),多对多( 一种Product可以被多个User购买
一个User可以购买多种Product,要实现多对多关系,必须有一张中间表 user_product 用于维护 User和Product之间的关系 )
/**多对一设置方式:
使用many-to-one 标签设置多对一关系
name="category" 对应Product类中的category属性
class="Category" 表示对应Category类
column="cid" 表示指向 category_表的外键**/
<?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.how2java.pojo">
<class name="Product" table="product_">
<id name="id" column="id">
<generator class="native">
</generator>
</id>
<property name="name" />
<property name="price" />
<many-to-one name="category" class="Category" column="cid" />
</class>
</hibernate-mapping>
/**
<set name="products" lazy="false">
<key column="cid" not-null="false" />
<one-to-many class="Product" />
</set>
<set>用于设置一对多(多对多也是他)关系,也可以用list,设置稍复杂点,这里使用简单的set来入门。
name="products" 对应 Category类中的products集合属性
lazy="false" 表示不使用延迟加载。关于延迟加载,请参考关系的延迟加载
<key column="cid" not-null="false" /> 表示外键是cid,可以为空
<one-to-many class="Product" /> 表示一对多所对应的类是Product**/
<?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.how2java.pojo">
<class name="Category" table="category_">
<id name="id" column="id">
<generator class="native">
</generator>
</id>
<property name="name" />
<set name="products" lazy="false">
<key column="cid" not-null="false" />
<one-to-many class="Product" />
</set>
</class>
</hibernate-mapping>
延迟加载
hibernate中的延迟加载(lazyload)分属性的延迟加载和关系的延迟加载
延迟加载,也叫懒加载,它是Hibernate为提高程序执行效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
Hibernate中主要是通过代理(proxy)机制来实现延迟加载。它的具体过程:Hibernate从数据库获取某一个对象数据时,或获取某一个对象的集合属性值时,或获取某一个对象所关联的另一个对象时,由于没有使用到该对象的数据,hibernate并不从数据库加载真正的数据,而只是为该对象创建一个代理对象来代表这个对象,这个对象上的所有属性都是默认值;只有在真正需要使用该对象的数据时才创建这个真实对象,真正从数据库中加载它的数据,这样在某些情况下,就可以提高查询效率。
有如下程序代码:
User user = (User) session.load(clazz, id); // 直接返回的是代理对象
System.out.println(user.getId()); // 没有发送sql语句到数据库加载
user.getName(); // 创建真实的User实例,并发送sql语句到数据库中
延时加载不是问题,是为了解决问题,在hibernate中,有两种关系是相对存在的,就是一对多和多对一,如果同时使用这两种关系,并且不使用延迟加载会很麻烦的。
比如:学生和班级的关系。
注意:
1、不能判断User = null;代理对象不可能为空
代理对象的限制:和代理关联的session对象,如果session关闭后访问代理则抛异常。session关闭之前访问数据库
2、getId()方法不行因为参数为ID,getClass()方法不用访问数据库就可以得到的数据
Hibernate中默认采用延迟加载的情况主要有以下几种
1、当调用session上的load()加载一个实体时,会采用延迟加载。
2、当session加载某个实体时,会对这个实体中的集合属性值采用延迟加载。
3、当session加载某个实体时,会对这个实体所有单端关联的另一个实体对象采用延迟加载。
提高效率,主要是对属性(在数据库中存在相应的表)进行延迟加载(load),在第一次查询的时候,只查询当前的表,当用到延迟加载的对象时(非延迟加载会查询所有关联属性的表),会先从缓存中去找延迟加载的对象(如果session已经关闭,会抛出SessionClosedException),如果对象已经在缓存中,则直接从缓存获取,如果对象不在缓存中,则进入数据库查找,所以,延迟加载是为了提高效率,如果在hibernate映射文件lazy设置成false,用load则和用get效果一样。
级联
什么是级联? 简单的说,没有配置级联的时候,删除分类,其对应的产品不会被删除。 但是如果配置了恰当的级联,
那么删除分类的时候,其对应的产品都会被删除掉。
Oracle数据库中,外键约束只允许级联删除,不允许级联更新,因此,如果想要实现主表数据更新后,子表外键自动更新,只能取消外键关系,通过前端程序来维护实现完整引用
四种级联:
级联有4种类型:
all:所有操作都执行级联操作;
none:所有操作都不执行级联操作;
delete:删除时执行级联操作;
save-update:保存和更新时执行级联操作;
级联通常用在one-many和many-to-many上,几乎不用在many-one上。
级联删除:
<?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.how2java.pojo">
<class name="Category" table="category_">
<id name="id" column="id">
<generator class="native">
</generator>
</id>
<property name="name" />
<set name="products" cascade="delete" lazy="false">
<key column="cid" not-null="false" />
<one-to-many class="Product" />
</set>
</class>
</hibernate-mapping>
缓存
一级缓存: hibernate默认是开启一级缓存的,一级缓存存放在session上。 不能关闭,自动启动,二级缓存才需要配置,参考二级缓存。
二级缓存:二级缓存是在SessionFactory上
在hibernate.cfg.xml中开启二级缓存的配置
hibernate本身不提供二级缓存,都是使用第三方的二级缓存插件
这里使用的是 EhCache提供的二级缓存
<?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>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test?characterEncoding=GBK</property>
<property name="connection.username">root</property>
<property name="connection.password">admin</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<mapping resource="com/how2java/pojo/Product.hbm.xml" />
<mapping resource="com/how2java/pojo/Category.hbm.xml" />
<mapping resource="com/how2java/pojo/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
Hibernate有两种方式获得session
Hibernate有两种方式获得session,分别是:
openSession和getCurrentSession
他们的区别在于
- 获取的是否是同一个session对象
openSession每次都会得到一个新的Session对象
getCurrentSession在同一个线程中,每次都是获取相同的Session对象,但是在不同的线程中获取的是不同的Session对象 - 事务提交的必要性
openSession只有在增加,删除,修改的时候需要事务,查询时不需要的
getCurrentSession是所有操作都必须放在事务中进行,并且提交事务后,session就自动关闭,不能够再进行关闭
hibernate注解
1:类注解
/**在注解示例-注解方式的Product中,Product类声明前面有两个注解:@Entity 和 @Table(name = "product_")
@Entity 表示这是一个实体类,用于映射表
@Table(name = "product_") 表示这是一个类,映射到的表名:product_
**/
@Entity
@Table(name = "product_")
public class Product {
int id;
String name;
float price;
}
2:属性注解
/**
在get方法上注解
@Id 表示这是主键
@GeneratedValue(strategy = GenerationType.IDENTITY) 表示自增长方式使用mysql自带的
@Column(name = "id") 表示映射到字段id
**/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
public int getId() {
return id;
}
3:关系注解
/**多对一注解:
注: 分类的getName上并没有加上@Column(name="name"),也可以达到映射的效果。 因为getName方法默认会被认为是字段映射。 除非加上了@Transient 才表示不进行映射
@ManyToOne 表示多对一关系
@JoinColumn(name="cid") 表示关系字段是cid
**/
@ManyToOne
@JoinColumn(name="cid")
public Category getCategory() {
return category;
}
/**一对多注解
@OneToMany 表示一对多,fetch=FetchType.EAGER 表示不进行延迟加载(FetchType.LAZY表示要进行延迟加载)
@JoinColumn(name="cid") 表示映射字段
**/
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name="cid")
public Set<Product> getProducts() {
return products;
}
/**多对多注解
**/
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinTable(
name="user_product",
joinColumns=@JoinColumn(name="pid"),
inverseJoinColumns=@JoinColumn(name="uid")
)
public Set<User> getUsers() {
return users;
}
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinTable(
name="user_product",
joinColumns=@JoinColumn(name="uid"),
inverseJoinColumns=@JoinColumn(name="pid")
)
public Set<Product> getProducts() {
return products;
}
注解手册:
类注解:
@Entity —— 将一个类声明为一个实体bean(即一个持久化POJO类)
@Table —— 注解声明了该实体bean映射指定的表(table),目录(catalog)和schema的名字
属性相关注解:
@Id —— 注解声明了该实体bean的标识属性(对应表中的主键)。
@Column —— 注解声明了属性到列的映射。该注解有如下的属性
name 可选,列名(默认值是属性名)
unique 可选,是否在该列上设置唯一约束(默认值false)
nullable 可选,是否设置该列的值可以为空(默认值false)
insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true)
updatable 可选,该列是否作为生成的update语句中的一个列(默认值true)
columnDefinition 可选,为这个特定列覆盖sql ddl片段(这可能导致无法在不同数据库间移植)
table 可选,定义对应的表(默认为主表)
length 可选,列长度(默认值255)
precision 可选,列十进制精度(decimal precision)(默认值0)
scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)
@GeneratedValue —— 注解声明了主键的生成策略。该注解有如下属性
strategy 指定生成的策略(JPA定义的),这是一个GenerationType。默认是GenerationType. AUTO
GenerationType.AUTO 主键由程序控制
GenerationType.TABLE 使用一个特定的数据库表格来保存主键
GenerationType.IDENTITY 主键由数据库自动生成(主要是自动增长类型)
GenerationType.SEQUENCE 根据底层数据库的序列来生成主键,条件是数据库支持序列。(这个值要与generator一起使用)
generator 指定生成主键使用的生成器(可能是orcale中的序列)。
@SequenceGenerator —— 注解声明了一个数据库序列。该注解有如下属性
name 表示该表主键生成策略名称,它被引用在@GeneratedValue中设置的“gernerator”值中
sequenceName 表示生成策略用到的数据库序列名称。
initialValue 表示主键初始值,默认为0.
allocationSize 每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50.
关系相关注解:
@ManyToOne 设置多对一关联
方法一
@ManyToOne(cascade={CasCadeType.PERSIST,CascadeType.MERGE})
@JoinColumn(name="外键")
public 主表类 get主表类(){return 主表对象}
方法二
@ManyToOne(cascade={CascadeType.PERSIST,CascadeType.MERGE})
@JoinTable(name="关联表名",
joinColumns = @JoinColumn(name="主表外键"),
inverseJoinColumns = @JoinColumns(name="从表外键")
)
@OneToMany 设置一对多关联。
方法一 。
“一端”配置
@OneToMany(mappedBy="“多端”的属性")
public List<“多端”类> get“多端”列表(){return “多端”列表}
“多端”配置参考@ManyToOne.
方法二
“一端”配置
@OneToMany(mappedBy="“多端”的属性")
@MapKey(name="“多端”做为Key的属性")
public Map<“多端”做为Key的属性的类,主表类> get“多端”列表(){return “多端”列表}
“多端”配置参考@ManyToOne.
方法三 使用这种配置,在为“一端”添加“多端”时,可以修改“多端”的外键。
“一端”配置
@OneToMany
@JoinColumn(name="“多端”外键")
public List<“多端”类> get“多端”列表(){return “多端”列表}
“多端”配置参考@ManyToOne.