引言
Hibernate提供了三种方式将POJO类变成PO类:
- 使用持久化注解(以JPA 标准注解为主,如果有一些特殊要求,则依然需要使用Hibernate 本身提供的注解)
- 使用JPA2 提供的XML 配置描述文件( XML deployment descriptor ),这种方式可以让Hibernate的PO 类与IPA 实体类兼容。但在实际开发中,很少有公司使用这种方式。
- 使用Hibernate 传统的XML映射文件 (* .hbm. xm l 文件的形式) ,由于这种方式是传统Hibernate的推荐方式,因此依然有少数企业会采用这种方式。
因为第一种方式为主流,那么我们就学习一下!
持久化注解
@Entity
被该注解修饰的POJO 就是一个实体。使用该注解时可指定一个name 属性, name 属性指定该实体类的名称,但大部分时候无须指定该属性,因为系统默认以该类的类名作为实体!
@Entity//注释声明该类为Hibernate持久化类
@Table
@Table(name="table1")//指定映射的表
@UniqueConstraint
用于为数据表定义唯一约束。它的用法非常简单,使用该注解时可以指定如下唯一的属性。
- columnName:该属性的值是一个字符串数组,每个字符串元素代表一个数据列。
@Index
用于为数据表定义索引。
@Proxy
该注解的proxyClass 属性指定一个接口,在延迟加载时作为代理使用,也可以在这里指定该类自己的名字。
@DynamicInsert
指定用于插入记录的insert 语句是否在运行时动态生成,并且只插入那些非空宇段。该属性的值默认是false 。开启该属性将导致Hibernate 需要更多时间来生成SQL 语句。
DynamicUpdate
指定用于更新记录的update 语句是否在运行时动态生成,并且只更新那些改变过的字段。该属性的值默认是false 。开启该属性将导致Hibernate 需要更多的时间来生成SQL语句。
@SelectBeforeUpdate
指定Hibernate 在更新( update )某个持久化对象之前是否需要先进行一次查询( select ) 。如果将该注解的value 值设为true ,则Hibernate 可以保证只有当持久化对象的状态被修改过时,才会使用update i吾句来保存其状态(即使程序显式使用saveOrUpdate()来保存该对象,但如果Hibernate 查询到对应记录与该持久化对象的状态相同,也不会使用update语句来保存其状态) 。该注解的value 值默认为false 。
@PolymorphismType
当采用TABLE_PER_CLASS 继承映射策略时,该注解用于指定是否需要采用隐式多态查询。该注解的value 的默认值为PolymorphismType.IMPLICIT ,即支持隐式多态查询。
@Where
该注解的clause 属性可指定一个附加的SQL 语句过滤条件(类似于添加where 子句)如果一旦指定了该注解,则不管采用load()、get()还是其他查询方法,只要试图加载该持久化类的对象时,该where 条件就会生效。也就是说,只有符合该where 条件的记录才会被加载。
@BatchSize
当H ibernate 抓取集合属性或延迟加载的实体时,该注解的size 属性指定每批抓取的实例数。
@OptimisticLocking
该注解的type 属性指定乐观锁定策略。Hibernate 支持OptimisticLockType .ALL 、OptimisticLockType.DIRTY 、OptimisticLockType.NONE OptimisticLockType. VERSION 这4 个枚举值。默认值OptimisticLockType.VERSION。
@Check
该注解可通过constraints 指定一个SQL 表达式,用于为应持久化类所对应的表指定一个Check 约束。
@Subselect
该注解用于映射不可变的、只读实体。通俗地说,就是将数据库的子查询映射成Hibernate 持久化对象。当需要使用视图(其实质就是一个查询〉来代替数据表时,该注解比较有用。
映射属性
@Column
为了指定某个属性所映射的数据列的详细信息,如列名,列名字段长度等,在实体类中使用 @Column 修饰该属性。
@Formula
该注解的value 属性可指定一个SQL 表达式,指定该属性的值将根据表达式来计算。持久化类对应的表中没有和计算属性对应的数据列-------因为该属性值是动态计算出来的,无需保存到数据库。
import javax.persistence.*;
import javax.persistence.*;
import org.hibernate.annotations.Formula;
@Entity
@Table(name="news_inf")
public class News
{
// 消息类的标识属性
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
// 消息标题
private String title;
// 消息内容
private String content;
// 消息全部内容,由系统根据公式生成
@Formula("(select concat(nt.title,nt.content)"
+ "from news_inf nt where nt.id= id)")
private String fullContent;
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// title的setter和getter方法
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return this.title;
}
// content的setter和getter方法
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return this.content;
}
// fullContent的setter和getter方法
public void setFullContent(String fullContent)
{
this.fullContent = fullContent;
}
public String getFullContent()
{
return this.fullContent;
}
}
解释:
上面PO 类的创!Content 属性并不需要采用数据列保存, 该属性的值将由系统根据SQL 表达式来生成,所以程序映射fullContent 属性时使用了@Formula 修饰。
MySQL教程之concat以及group_concat的用法
@Generated
设置该属性映射的数据列的值是否由数据库生成, 该注解的value 属性可以接受GenerationTime.NEVER (不由数据库生成) 、GenerationTime.INSERT (该属性值在执行insert语句时生成, 但不会在执行update 语句时重新生成) 和Generati onTime .ALWA YS (该属性值在执行insert 和update 语句时都会被重新生成)这三个值的其中之一。
import javax.persistence.*;
import javax.persistence.*;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
@Entity
@Table(name="news_inf")
public class News
{
// 消息类的标识属性
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
// 消息标题
private String title;
// 消息内容
private String content;
// 指定@Generated的value为ALWAYS,表明该属性的值由数据库生成,
// Hibernate会在每次执行insert、update时执行select语句来查询获取该属性的值
@Generated(GenerationTime.ALWAYS)
private String fullContent;
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// title的setter和getter方法
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return this.title;
}
// content的setter和getter方法
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return this.content;
}
// fullContent的setter和getter方法
public void setFullContent(String fullContent)
{
this.fullContent = fullContent;
}
public String getFullContent()
{
return this.fullContent;
}
}
import javafx.fxml.FXMLLoader;
import org.hibernate.*;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.query.Query;
import org.hibernate.cfg.Configuration;
import javax.imageio.spi.ServiceRegistry;
import javax.persistence.metamodel.EntityType;
import java.util.Map;
public class Main {
public static void main(final String[] args) throws Exception {
// 实例化Configuration,
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
// 创建Session
Session sess = sf.openSession();
// 开始事务
Transaction tx = sess.beginTransaction();
// 创建消息对象
News n = new News();
// 设置消息标题和消息内容
n.setTitle("疯狂Java联盟成立了");
n.setContent("疯狂Java联盟成立了,"
+ "网站地址http://www.crazyit.org");
// 保存消息
sess.save(n);
// News n = (News)sess.get(News.class , 1);
// // 输出fullContent值
// System.out.println(n.getFullContent());
// 提交事务
tx.commit();
// 关闭Session
sess.close();
sf.close();
}
}
使用@Transient修饰不想持久保存属性
在默认情况下,持久化类的所有属性会自动映射到数据表的数据列。如果在实际应用中不想持久保存某些属性,则可以考虑使用@Transient 来修饰它们。
import javax.persistence.*;
@Entity
@Table(name="news_inf")
public class News
{
// 消息类的标识属性
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
// 消息标题
// @Column指定该属性映射的列信息,此处指定了列名、长度
@Column(name="news_title" , length=50)
private String title;
// 消息内容
@Transient
private String content;
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// title的setter和getter方法
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return this.title;
}
// content的setter和getter方法
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return this.content;
}
}
import org.hibernate.*;
import org.hibernate.query.Query;
import org.hibernate.cfg.Configuration;
import javax.persistence.metamodel.EntityType;
import java.util.Map;
public class Main {
private static final SessionFactory ourSessionFactory;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
ourSessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() throws HibernateException {
return ourSessionFactory.openSession();
}
public static void main(final String[] args) throws Exception {
final Session session = getSession();
try {
// 开始事务
Transaction tx = session.beginTransaction();
// 创建消息对象
News n = new News();
// 设置消息标题和消息内容
n.setTitle("疯狂Java联盟成立了");
n.setContent("疯狂Java联盟成立了,"
+ "网站地址http://www.crazyit.org");
// 保存消息
session.save(n);
// 提交事务
tx.commit();
} finally {
session.close();
}
}
}
使用@ Tra nsient 修饰了content ,这意味着Hiberna t e 将该持久化类映射到底层数据表时,content 不会映射到任何数据列; 当保存一个N ews 实体时,在N ews 实体中content 值不会被保存到数据中。
使用@Enumerated修饰枚举类型的属性
在有些极端的情况下, 持久化类的属性不是普通的Java 类型,而是一个枚举类型,这意味着该属性只能接受有限的几个固定值。在这种情况下,可以考虑使用@E num erated 修饰实体类中枚举类型的属性。例如, 如下程序定义了一个Seaso n 枚举类。
package Season;
public enum Season
{
春季,夏季,秋季,冬季
}
对于枚举值而言,既可在程序中通过枚举值的名字来代表,也可使用枚举值的序号来代表。假如想在程序中使用Season 枚举值表示春季, 则既可用“春季”枚举值的名称代表,也可用枚举值的序号。代表同样地底层数据库既可保存枚举值名称来代表枚举值,也可保存枚举值序号来代表枚举值,这一点可通过@Enumerated 的value 属性来指定.当@ Enumerated 的value 属性为EnumType.STRING时,底层数据库保存枚举值的名称;当@ Enumerated 的value属性为EnumType.ORDINAL 时,底层数据库保存枚举值的序号。
import Season.Season;
import org.hibernate.*;
import org.hibernate.query.Query;
import org.hibernate.cfg.Configuration;
import javax.persistence.metamodel.EntityType;
import java.util.Map;
public class Main {
private static final SessionFactory ourSessionFactory;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
ourSessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() throws HibernateException {
return ourSessionFactory.openSession();
}
public static void main(final String[] args) throws Exception {
final Session session = getSession();
try {
// 开始事务
Transaction tx = session.beginTransaction();
// 创建消息对象
News n = new News();
// 设置消息标题和消息内容
n.setTitle("疯狂Java联盟成立了");
n.setContent("疯狂Java联盟成立了,"
+ "网站地址http://www.crazyit.org");
// 保存消息
n.setHappenSeason(Season.夏季);
session.save(n);
// 提交事务
tx.commit();
} finally {
session.close();
}
}
}
使用@LOb,@Basic修饰大数据类型的属性
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--加载数据库驱动-->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate?serverTimezone=UTC</property>
<property name="connection.username">root</property>
<property name="connection.password">123</property>
<!--指定数据库方言-->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- DB schema will be updated if needed -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!--在控制台显示SQL语句-->
<property name="show_sql">true</property>
<!--将SQL脚本中语句格式化在输出-->
<property name="hibernate.format_sql">true</property>
<!--罗列所有的持久化类-->
<mapping class="Person"/>
</session-factory>
</hibernate-configuration>
import javax.persistence.*;
@Entity
@Table(name="person_inf")
public class Person
{
@Id // 用于修饰标识属性
// 指定该主键列的主键生成策略
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
// @Column指定该属性映射的列信息,此处指定了列名、长度
@Column(name="person_name" , length=50)
private String name;
@Lob
@Basic(fetch=FetchType.LAZY)
@Column(nullable=true)
private byte[] pic;
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// pic的setter和getter方法
public void setPic(byte[] pic)
{
this.pic = pic;
}
public byte[] getPic()
{
return this.pic;
}
}
import org.hibernate.*;
import org.hibernate.query.Query;
import org.hibernate.cfg.Configuration;
import javax.persistence.metamodel.EntityType;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;
public class Main {
private static final SessionFactory ourSessionFactory;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
ourSessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() throws HibernateException {
return ourSessionFactory.openSession();
}
public static void main(final String[] args) throws Exception {
final Session session = getSession();
try {
Transaction tx = session.beginTransaction();
// 创建Person对象
Person person = new Person();
// 为Person对象的属性设置值
person.setName("crazyit.org");
File file = new File("logo.jpg");
byte[] content = new byte[(int)file.length()];
new FileInputStream(file).read(content);
person.setPic(content);
// 保存Person对象
session.save(person);
} finally {
session.close();
}
}
}
Person 实体设置b yte []类型的属性值,程序使用IO 流读取了磁盘上的一张图片文件,并将图片文件的数据放入b yte [ ]数组中,然后将该byte [ ]数组的值作为setPic()方法的参数传入,这样就为Pe r son 实体的pi e 属性设置成功。
为了提高效率
有一种可能的情况是,程序只是需要访问该P ers on 对象的name 属性,根本不关心Person 对象的pie 属性,那么Hibernate 就白白浪费时间来加载pie 属性了,这显然不是一种好的做法。
为了改变这种情况, 程序希望有一种机制可以做到: Hibernate 加载Person 对象时并不立即加载它
的pie 属性,而是只加载一个“虚拟”的代理,等到程序真正需要pie 属性时才从底层数据表中加载数
据一一这就是典型的代理模式。Hi b ernate 为这种机制提供了支持,并将这种机制称为延迟加载,只要在开发实体时使用@Basic 修饰该属性即可。
使用@Temporal修饰日期类型的属性
import org.hibernate.*;
import org.hibernate.query.Query;
import org.hibernate.cfg.Configuration;
import javax.persistence.metamodel.EntityType;
import java.util.Map;
public class Main {
private static final SessionFactory ourSessionFactory;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
ourSessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() throws HibernateException {
return ourSessionFactory.openSession();
}
public static void main(final String[] args) throws Exception {
final Session session = getSession();
try {
Transaction tx = session.beginTransaction();
// 创建Person对象
Person person = new Person();
// 为Person对象的属性设置值
person.setName("crazyit.org");
person.setBirth(new java.util.Date());
// 保存Person对象
session.save(person);
} finally {
session.close();
}
}
}
import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name="person_inf")
public class Person
{
@Id // 用于修饰标识属性
// 指定该主键列的主键生成策略
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
// @Column指定该属性映射的列信息,此处指定了列名、长度
@Column(name="person_name" , length=50)
private String name;
@Temporal(TemporalType.DATE)
private Date birth;
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// birth的setter和getter方法
public void setBirth(Date birth)
{
this.birth = birth;
}
public Date getBirth()
{
return this.birth;
}
}