Hibernate与MyBatis都是流行的持久层开发框架,前一遍介绍了怎样在SpringBoot中集成MyBatis,本篇来介绍如何集成Hibernate作为DAO层。
Hibernate 是一个高性能的对象/关系映射(ORM)持久化存储和查询的服务,不仅负责从Java类到数据库表的映射
(还包括从Java数据类型到SQL数据类型的映射),还提供了面向对象的数据查询检索机制,从而极大地缩短了手动处理SQL和JDBC上的开发时间。
同时,Hibernate还实现了JPA规范,在SpringBoot中,JPA的默认实现就是使用的Hibernate。
JPA和Hibernate
在讲解如何在SpringBoot中使用Hibernate框架之前,先要弄清几个基本概念以及它们之间的关系。
JPA Hibernate Spring Data Spring Data JPA JPA
JPA(Java Persistence API)是Sun官方提出的Java持久化规范,它为Java开发人员提供了一种对象/关系映射工具来管理Java应用中的关系数据。
上面已经描述很清楚了,JPA是一种规范和标准,也就是类似于Java中的接口,由Sun公司官方定义,但是并没有指定谁来实现。
现在几个主要的JPA实现技术有Hibernate、EclipseLink、OpenJPA等。
Hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的ORM框架。
Hibernate可以自动生成SQL语句、自动执行,从而使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
实际上,JPA规范制定过程中就是借鉴了Hibernate等这些开源的持久框架,也就是说Hibernate的出现比JPA还要早些。
在Hibernate中使用的注解就是JPA注解,Hibernate实现了JPA规范。
Spring Data
Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得数据库的访问变得方便快捷,
并支持map-reduce框架和云计算数据服务。此外,它还支持基于关系型数据库的数据服务,如Oracle RAC等。
所以Spring Data本身就是一个开源的框架。
Spring Data JPA
上面说过Spring Data是一个开源框架,在这个框架中Spring Data JPA只是这个框架中的一个模块。
它可以极大的简化JPA的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。
好了,现在你应该对这些概念有一个基本认识了,接下来进入正题,演示如何在SpringBoot中使用Hibernate。
maven依赖
第一步添加maven依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId> mysql</groupId>
<artifactId> mysql-connector-java</artifactId>
<version> ${mysql-connector.version}</version>
<scope> runtime</scope>
</dependency>
<dependency>
<groupId> com.alibaba</groupId>
<artifactId> druid</artifactId>
<version> ${druid.version}</version>
</dependency>
注意,使用mysql数据库来演示的话还要添加数据库驱动包,另外还添加了druid连接池。
准备数据库
如何安装mysql,配置用户名和密码之类我就不在这里讲了,请自行google。
下面是我的schema.sql文件,创建一个pos数据库,并创建一个article表,插入几条初始数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- Dumping database structure for concretepage
CREATE
DATABASE IF NOT EXISTS ` pos ` default charset utf8 COLLATE utf8_general_ci ;
SET
FOREIGN_KEY_CHECKS = 0 ;
USE
` pos ` ;
-- Dumping structure for table concretepage.articles
CREATE TABLE IF NOT EXISTS ` articles `
(
` article_id ` int
(
5
) NOT NULL AUTO_INCREMENT ,
` title ` varchar
(
200
) NOT NULL ,
` category ` varchar
(
100
) NOT NULL ,
PRIMARY KEY
(
` article_id `
)
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT = '文章表' ;
-- Dumping data for table concretepage.articles: ~3 rows (approximately)
INSERT INTO ` articles ` ( ` article_id ` , ` title ` , ` category ` )
VALUES ( 1 , 'Java Concurrency' , 'Java' ),
( 2 , 'Hibernate HQL ' , 'Hibernate' ),
( 3 , 'Spring MVC with Hibernate' , 'Spring' );
注意,Hibernate可以根据实体类自动创建表,本篇我不介绍这个功能,还是手动创建表结构。
修改配置文件
数据库创建好后,修改application.yml配置文件,新增如下数据库连接配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
################### spring配置 ###################
spring :
profiles :
active : dev
jpa :
properties :
hibernate :
dialect : org.hibernate.dialect.MySQLDialect
new_generator_mappings : false
format_sql : true
datasource :
url : jdbc:mysql://127.0.0.1:3306/pos?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
username : root
password : 123456
实体类
article表对应的实体类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Table ( name = "articles" )
public class Article implements Serializable {
private static final long serialVersionUID = 1L ;
@Id
@GeneratedValue ( strategy = GenerationType . AUTO )
@Column ( name = "article_id" )
private int articleId ;
@Column ( name = "title" )
private String title ;
@Column ( name = "category" )
private String category ;
// 省略getter/setter
}
DAO设计
先创建一个DAO的接口类:
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IArticleDAO {
List < Article > getAllArticles ();
Article getArticleById ( int articleId );
void addArticle ( Article article );
void updateArticle ( Article article );
void deleteArticle ( int articleId );
boolean articleExists ( String title , String category );
}
然后写一个实现类,在这类中注入EntityManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Repository
public class ArticleDAO implements IArticleDAO {
@PersistenceContext
private EntityManager entityManager ;
@Override
public Article getArticleById ( int articleId ) {
return entityManager . find ( Article . class , articleId );
}
@SuppressWarnings ( "unchecked" )
@Override
public List < Article > getAllArticles () {
String hql = "FROM Article as atcl ORDER BY atcl.articleId" ;
return ( List < Article > ) entityManager . createQuery ( hql ). getResultList ();
}
@Override
public void addArticle ( Article article ) {
entityManager . persist ( article );
}
@Override
public void updateArticle ( Article article ) {
Article artcl = getArticleById ( article . getArticleId ());
artcl . setTitle ( article . getTitle ());
artcl . setCategory ( article . getCategory ());
entityManager . flush ();
}
@Override
public void deleteArticle ( int articleId ) {
entityManager . remove ( getArticleById ( articleId ));
}
@Override
public boolean articleExists ( String title , String category ) {
String hql = "FROM Article as atcl WHERE atcl.title = ? and atcl.category = ?" ;
int count = entityManager . createQuery ( hql ). setParameter ( 0 , title )
. setParameter ( 1 , category ). getResultList (). size ();
return count > 0 ;
}
}
业务Service
编写业务逻辑Service类,注入DAO类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Service
public class ArticleService {
@Resource
private IArticleDAO articleDAO ;
public Article getArticleById ( int articleId ) {
Article obj = articleDAO . getArticleById ( articleId );
return obj ;
}
public List < Article > getAllArticles () {
return articleDAO . getAllArticles ();
}
public synchronized boolean addArticle ( Article article ) {
if ( articleDAO . articleExists ( article . getTitle (), article . getCategory ())) {
return false ;
} else {
articleDAO . addArticle ( article );
return true ;
}
}
public void updateArticle ( Article article ) {
articleDAO . updateArticle ( article );
}
public void deleteArticle ( int articleId ) {
articleDAO . deleteArticle ( articleId );
}
}
编写测试用例
OK,一切写完后,就来编写我们的测试用例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RunWith ( SpringRunner . class )
@SpringBootTest
public class ApplicationTests {
private static final Logger log = LoggerFactory . getLogger ( ApplicationTests . class );
@Resource
private ArticleService articleService ;
/**
* 测试增删改查
*/
@Test
public void test () {
Article article = articleService . getArticleById ( 1 );
assertThat ( article . getTitle (), is ( "Java Concurrency" ));
List < Article > list = articleService . getAllArticles ();
assertThat ( list , notNullValue ());
assertThat ( list . size (), is ( 3 ));
boolean flag = articleService . addArticle ( article );
assertThat ( flag , is ( false ));
article . setTitle ( "Python Concurrency" );
articleService . updateArticle ( article );
Article article1 = articleService . getArticleById ( 1 );
assertThat ( article1 . getTitle (), is ( "Python Concurrency" ));
articleService . deleteArticle ( 1 );
Article article2 = articleService . getArticleById ( 1 );
assertThat ( article2 , nullValue ());
}
}
上面先查询id=1的文章,看看标题是否为Java Concurrency,然后查询所有文章列表,之后更新文章标题后再次查询看看是否更新成功。
最后删除这篇文章,再次查询结果为null。
运行结果green bar,测试通过。
更多Hibernate相关文档,请参考 官网 。
GitHub源码
springboot-hibernate