Hibernate框架简化了java应用程序与数据库交互的开发。Hibernate是一个开源,轻量级的ORM(对象关系映射) 工具。了解更多Hibernate,访问Hibernate官网。
ORM 工具简化了数据创建,数据处理和数据访问。它是将对象映射到数据库中存储的数据(表)的编程技术。
Hibernate框架的优点:
Hibernate架构包括许多对象持久对象,会话工厂,事务工厂,连接工厂,会话,事务等。
Hibernate框架使用许多对象会话工厂,会话,事务等以及现有的Java API,如JDBC(Java数据库连接),JTA(Java事务API)和JNDI(Java命名目录接口)。
Hibernate体系结构的要素如下:
会话工厂(SessionFactory)
SessionFactory是ConnectionProvider的会话和客户端工厂。它拥有数据的二级缓存(可选)。org.hibernate.SessionFactory接口提供了工厂方法来获取Session的对象。
会话(Session)
Session对象提供应用程序和存储在数据库中的数据之间的接口。它是一个短生命周期的对象并包装JDBC连接。它是事务,查询和标准的工厂。 它拥有一级缓存(强制性)数据。org.hibernate.Session接口提供插入,更新和删除对象的方法。它还提供了事务,查询和标准的工厂方法。
事务(Transaction)
事务对象指定工作的原子单位,它是一个可选项。org.hibernate.Transaction接口提供事务管理的方法。
连接提供者(ConnectionProvider)
它是一个JDBC连接工厂。它从DriverManager或DataSource抽象出来的应用程序。它是一个可选项。
事务工厂(TransactionFactory)
它是一个事务工厂,是一个可选项。
3.1 创建一个Maven项目,目录结构如下:
3.2 在pom.xml里添加要依赖的jar配置
UTF-8
5.2.1.Final
org.hibernate
hibernate-core
${hibernate.version}
mysql
mysql-connector-java
8.0.12
junit
junit
4.11
test
3.3 创建表结构(如果这里不创建,hibernate可以自动生成表结构)
3.4 创建实体类HUser
public class HUser implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.5 创建实体类的映射文件Huser.hbm.xml
3.6 创建配置文件hibernate.cfg.xml
com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/hibernatetest?useSSL=false&serverTimezone=UTC
root
root
org.hibernate.dialect.MySQL5Dialect
true
true
update
3.7 创建管理Session的辅助类
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
sessionFactory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一 个SessionFactory并从中获取Session实例,
* 而Session不是线程安全的。Session中包含了数 据库操作相关的状态信息,如果多个线程同时使用一个Session实例进行CRUD,就可能导致数据存取的混乱。
* java.lang.ThreadLocal,在编写多线程程序时提供了一种解决方案。
* 线程局部变量(ThreadLocal)就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
* 从线程的角度看,就好像每 一个线程都完全拥有一个该变量。 ThreadLocal这个类本身不是代表线程要访问的变量,这个类的成员变量才是。
* JDK1.5给ThreadLocal加了泛型功能,即是 ThreadLocal,这个泛型T即是要线程的本地变量。线程通过ThreadLocal的get和set方法去访问这个变量T。
* ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
*/
private static final ThreadLocal threadLocal = new ThreadLocal();
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null) {
session = sessionFactory.openSession();
threadLocal.set(session);
}
return session;
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session != null)
session.close();
threadLocal.set(null);
}
public static void shutdown() {
getSessionFactory().close();
}
}
3.8 创建测试类
public class HibernateTest {
private Session session;
private Transaction tx;
@Before
public void before() {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
}
@Test
public void test() {
try {
HUser hUser = new HUser();
hUser.setName("hibernate demo");
session.save(hUser);// 这里操作的是java对象
tx.commit();
System.out.println("保存成功!");
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
System.out.println("保存失败!");
} finally {
HibernateUtil.closeSession();
}
}
@After
public void after() {
HibernateUtil.shutdown();
}
}
由上一个章节可以看出每一个实体类,都需要有一个以*.hbm.xml(*即类名)命名的映射文件,如果实体类很多,那么会有很多映射文件,这样很不友好。而Hibernate注解可以解决这个问题。如@Entity,@Id,@Table,@Column等。
下面四个常用注解及解释:
@Entity注释将此类标记为实体。
@Table注释指定要保留此实体的数据的表名。 如果不使用@Table注释,hibernate将使用类名作为表名称bydefault。
@Id注释标记此实体的标识符。
@Column注释指定此属性或字段的列的详细信息。如果未指定@Column注释,则属性名称将用作列名称bydefault。
Hibernate注解基于JPA 2规范,并支持所有功能。所有JPA注释都在javax.persistence.*包中定义。Hibernate EntityManager实现由JPA规范定义的接口和生命周期。
4.1 修改实体类HUser
@Entity
@Table(name = "huser")
public class HUser implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
/**
* @Id 定义为数据库的主键ID
* 建议不要在属性上引入注解,因为属性是private的,如果引入注解会破坏其封装特性,所以建议在getter方法上加入注解。
* @GeneratedValue ID的生成策略为自动生成
*/
@Id
@Column(name = "hid")
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "hname", length = 20)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.2 修改配置文件hibernate.cfg.xml
Hibernate中的一个对象存在于以下四个状态之中的一种:
以上几个状态在下面图中解释:
下面来看这几个状态的流转说明 -
当从一个实体创建一个新的Java对象时,该对象处于“短暂”状态。 Hibernate不知道它的存在,因为它独立于Hibernate的管理。
如果使用方法:get,load或find获取实体对象,则将获得一个等同于数据库中的1条记录的对象。 此对象处于Persistent状态。 它由Hibernate管理。
会话调用方法:save,saveOrUpdate和persist。 合并将短暂(Transient)对象置于Hibernate的管理之下,此对象转为持久化(Persistent)状态。 在使用的具体情况下,它向数据库插入或更新数据。
Session调用evict(..)或clear(),以便从处于Hibernate管理状态的对象处于关闭状态,并且这些对象处于分离(Detached)的状态。
使用update(..),saveOrUpdate(..),merge(..)将有助于重新连接分离对象。 在具体情况下,它会向数据库中创建更新或插入数据。 对象转回持久化(Persistent)状态。
Session调用方法:remove(..),delete(..)删除除记录并持久化对象。
可以在Hibernate中映射持久类的集合元素。 您需要从以下类型之一声明持久类中的集合类型:
java.util.List
java.util.Set
java.util.SortedSet
java.util.Map
java.util.SortedMap
java.util.Collection
org.hibernate.usertype.UserCollectionType
有很多,
6.1 集合映射中的映射列表(List)示例
List中存储字符串值而不是实体引用的示例,这就是为什么要在列表元素中使用element标签而不是one-to-many标签的元素。
持久化类
public class Article {
private int id;
private String title;
private String content;
private List comments;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public List getComments() {
return comments;
}
public void setComments(List comments) {
this.comments = comments;
}
}
持久化类的映射文件
更新hibernate.hnm.xml配置文件
存储数据类
@Test
public void test() {
try {
ArrayList comments = new ArrayList();
comments.add("文章写的真好~");
comments.add("文章写的棒棒哒~");
Article article = new Article();
article.setTitle("Hibernate入门到精通");
article.setContent("Hibernate入门到精通的内容是。。。");
article.setComments(comments);
session.persist(article);
tx.commit();
Query query = session.createQuery("from Article");
List articles = query.list();
Iterator itr = articles.iterator();
while (itr.hasNext()) {
Article art = itr.next();
System.out.println("Article Title: " + art.getTitle());
List cmts = art.getComments();
Iterator itr2 = cmts.iterator();
while (itr2.hasNext()) {
System.out.println(itr2.next());
}
}
session.close();
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}
}
如果持久化类具有包含实体引用的列表(List)对象,则需要使用一对多关联来映射列表元素。
在这里,我们使用博客发表文章应用场景,在博客上一篇文章有多个评论。
在这种情况下,一篇文章可以有多个评论,每个评论可能有自己的信息,这就是为什么在持久化类中使用列表(包含Comment类的引用)来表示一系列评论。
持久化类
public class Article {
private int id;
private String title;
private String content;
private List comments;
//getters and setters
}
public class Comment {
private int id;
private String commentcontent;
private String postedBy;
//getters and setters
}
持久化类的映射文件Article.hbm.xml和Comment.hbm.xml
配置文件hibernate.cfg.xml
存储数据的类
@Test
public void test() {
try {
ArrayList comments = new ArrayList();
Comment comment1 = new Comment();
comment1.setCommentcontent("文章写的真好~");
comment1.setPostedBy("Angelia头号粉!!!");
Comment comment2 = new Comment();
comment2.setCommentcontent("文章写的棒棒哒~");
comment2.setPostedBy("Angelia二号粉!!!");
comments.add(comment1);
comments.add(comment2);
Article article = new Article();
article.setTitle("Hibernate入门到精通");
article.setContent("Hibernate入门到精通的内容是。。。");
article.setComments(comments);
session.persist(article);
tx.commit();
Query query = session.createQuery("from Article");
List articles = query.list();
Iterator itr = articles.iterator();
while (itr.hasNext()) {
Article art = itr.next();
System.out.println("Article Title: " + art.getTitle());
List cmts = art.getComments();
Iterator itr2 = cmts.iterator();
while (itr2.hasNext()) {
System.out.println(itr2.next());
}
}
session.close();
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}
}
6.2 若持久类有List对象,我们可以通过列表或者bag元素在映射文件中映射。这个包(bag)就像List一样,但它不需要索引元素。
请注意,bag不是基于索引的,而list是基于索引的。
6.3 若持久类具有Set对象,可以在映射文件中使用set元素映射Set集合。set元素不需要索引元素。List和Set之间的区别是: Set只存储唯一的值。
6.4 Hibernate允许我们将Map元素与RDBMS进行映射。我们知道,List和Map是基于索引的集合。 在map的情况下,索引列作为键,元素列用作值。
持久化类
public class Article {
private int id;
private String title;
private String content;
private Map comments;
//getters and setters
}
持久化类的映射文件Article.hbm.xml
存储数据的类
@Test
public void test() {
try {
HashMap comments = new HashMap();
comments.put("文章写的真好~", "Angelia的头号粉丝");
comments.put("文章写的棒棒哒~", "Angelia的二号粉丝");
Article article = new Article();
article.setTitle("Hibernate入门到精通");
article.setContent("Hibernate入门到精通的内容是。。。");
article.setComments(comments);
session.persist(article);
Query query = session.createQuery("from Article ");
List list = query.list();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Article art = iterator.next();
System.out.println("Article title:" + art.getTitle());
Map map = art.getComments();
Set> set = map.entrySet();
Iterator> itercomment = set.iterator();
while (itercomment.hasNext()) {
Map.Entry entry = (Map.Entry) itercomment.next();
System.out.println("Comment name:" + entry.getKey());
System.out.println("Comment posted by:" + entry.getValue());
}
}
session.close();
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}
}
6.5 双向映射
持久化类
public class Article {
private int id;
private String title;
private String content;
private Set comments;
//getters and setters
}
public class Comment {
private int id;
private String commentcontent;
private String postedBy;
private Article article;
//getters and setters
}
持久化类的映射文件Article.hbm.xml和Comment.hbm.xml