话说:
框架,我来了,我看见,我征服!
前面一系列的JDBC“打怪升级”,走到了今天。笔者只想说,Web框架,终于轮到总结你们啦。
今天的主角是:Hibernate
目录:
一、Maven的下载、安装、工程创建
二、Hibernate初识、开发环境搭建
三、HibernateAPI操作数据库
四、总结
一、Maven的下载、安装、工程创建
后续所有的框架都是利用Maven来进行管理。Maven最大的作用就是结构清晰、导包方便。无论项目中各种包包有多少,都不会因为包包而增加整个项目的整体“体重”。
Maven安装和工程建立这里不再赘述,网上资料很多。
笔者使用IDE时IDEA(2017.2.5),整体布局如下:
二、Hibernate初识、开发环境搭建
What Hibernate?
Why Hibernate?
How?
个人理解:Hibernate是一种框架技术,大大简化了JDBC底层代码,不会SQL照样可以实现CURD。一句话,很简单!读者如果看了前面一系列JavaWebJDBC的文章,就会明白这一路优化过来,都是为今天埋足了伏笔。
最大的特点:ORM-Oriented Relational Mapping对象关系映射
、数据瞬时状态与持久化状态之间的转换
简单说:瞬时状态就是New对象时候的状态,每个对象对应数据库中的一行数据(之前使用反射机制实现);持久化就是把这个对象永久的保存到了数据库中。以前是我们自己写SQL语句,利用Java反射机制,实现CURD,现在都成为过去式啦!框架底层写好,直接调用即可!
这里不做过多概念解释,先来实现其功能,读者在回头查阅相关资料反复琢磨体会即可。
怎么用?
首先看官网。
1、导入Hibernate core包
在pom.xml中添加依赖,导入Hibernate -core包。
<!-- hibernate jar -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.10.Final</version>
</dependency>
导入时候,注意观察仓库变化。
只有看到这个包才证明真正下载完毕!
查看一下,这个时候光核心包就6M ,但是我们把项目复制后,发现才3K,这样我们给别人传项目的时候,就很容易。不像以前那样一大堆.jar包,对方会根据pom.xml自动下载。这就是Maven目前的第一个好处。体会到了嘛?
2、创建hibernate.cfg.xml配置文件
配置看官网,但是不齐全,所以需要下载整个包。下载5.2.10后,在对应位置找到project这个文件夹,找到这几个配置,复制hibernate.cfg.xml到项目sources目录下,删掉多余配置。根据hebernate.properties配置数据库相关信息:
结果如下:
<session-factory>
<!--配置方言-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--配置驱动-->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!--配置url-->
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/news_db</property>
<!--配置用户名-->
<property name="hibernate.connection.username">root</property>
<!--配置用户密码-->
<property name="hibernate.connection.password">119913</property>
</session-factory>
3 Junit测试
Junit测试好处明显。之前是通过main方法测试,一个类里面只能有一个main方法,需要不断的注释,一个一个测试。而Junit测试就很方便,加个注解即可。
可以测试多个方法,而且想测试哪个就测试哪个。
如果@Test用不了,就Alt+Enter导入包即可。
4、ORM
Object Relational Mapping 对象关系映射
1)编写对象的时候,以面向对象的方式处理数据
2)保存数据的时候却以关系型数据库的方式存储
有什么卵用?很简单,我们创建的实体类和数据库中的表之间是怎么关联起来的呢?Hibernate就用ORM搞定了这个问题。之前用JavaBean的时候,属性和数据库表的字段要保持一致,一旦不一致,就比较麻烦。But…..从此,摆脱这种束缚!
2中方法可以处理实体类和数据库表的关系:
法一:配置
法二:注解
用法一的话:在下载的hibernate包下面找到hbm.xml(搜索)文件,然后根据里面的配置。
注解简单,就用注解法,只需三步走:
1)在实体类News中加注解
package com.hmc.hibernate.model;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
/**
* User:Meice
* 2017/10/24
*/
@Entity
@Table(name = "t_news")
public class News {
private int id;
private String title;
private String author;
private String pic;
public News() {}
public News(int id, String title, String author, String pic) {
this.id = id;
this.title = title;
this.author = author;
this.pic = pic;
}
public News(String title, String author, String pic) {
this.title = title;
this.author = author;
this.pic = pic;
}
public News(String title, String author) {
this.title = title;
this.author = author;
}
@Id
@GenericGenerator(name = "myid",strategy = "increment")
@GeneratedValue(generator = "myid")
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 getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
@Override
public String toString() {
return "News{" +
"id=" + id +
", title='" + title + '\'' +
", author='" + author + '\'' +
", pic='" + pic + '\'' +
'}';
}
}
注解详细含义如下:
给类加注解
@Entity
@Table(name = “t_news”)
public class News {}
@Entity代表这个类是实体类
@Table表示选择哪个表
给字段加注解
@Id
@GenericGenerator(name = “myid”,strategy = “increment”)
@GeneratedValue(generator = “myid”)
public int getId() {
return id;
}
为什么在get方法上面添加注释?因为官方文档丫!而且,get一般是我们自己根据条件来限制和操作属性的。
其中,@ID表明是主键了;@GenericGenerator是主键生成策略
如果表字段与属性不一致,就给属性加column注解
@Column(name = )
要知道,之前我们做Java反射的时候,类的属性名要和字段名一模一样,对吧?如果不一样,那么反射的时候就找不到了,现在就摆脱了这个限制,数据库表的字段随意定义,加载一个注解即可。
补充:
主键生成策略:
native
increment
uuid
更多的主键生成策略参考:
http://blog.youkuaiyun.com/wanghuan203/article/details/7562395
2)配置实体类的映射
在hibernate.cfg.xml中增加如下配置
<!--配置关联映射-->
<mapping class="com.hmc.hibernate.model.News"></mapping>
这样,注解就正式生效啦!
3)测试
package com.hmc.hibernate;
import com.hmc.hibernate.model.News;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.Test;
import java.util.List;
/**
* User:Meice
* 2017/10/24
*/
public class HibernateTest02 {
@Test
public void test() {
//注册服务
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
//获取sessionFactory
SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
//查询t_news表中的数据
List<News> list = session.createQuery(" from News").list();//等价于select * from t_news;
System.out.println("总条数:"+list.size());
//关闭session
session.close();
}
}
这里的测试都是参考官方文档,不是空穴来风奥。以上代码很少,核心是得到session,怎么得到session?通过sessionFactory,如何得到sessionFactory?通过registry。固定的模式,类似JDBC连接一样。后续我们把这个封装起来。
5、自动创建数据表
前面测试的基础是数据库有这张表,如果数据库没有这个表呢?是否可以实现自动生成,木问题!
只要先创建实体类,做同样的注解==》配置实体映射和自动生成属性==》运行一个测试(这个测试随便哪个都行,但是就是要先创建工厂,然后创建session之类的)
比如:我们创建一个新闻评论实体类如下,此时数据库无此表。
package com.hmc.hibernate.model;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
/**
* User:Meice
* 2017/10/24
*/
@Entity
@Table(name = "t_comment")
public class Comment {
private int id;
private String name;
private String content;
@Id
@GenericGenerator(name = "myid",strategy = "increment")
@GeneratedValue(generator = "myid")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column(name = "com_name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
做如下配置:
<!--配置DDL-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!--配置关联映射-->
<mapping class="com.hmc.hibernate.model.News"></mapping>
<mapping class="com.hmc.hibernate.model.Comment"></mapping>
这个DDL配置是参考包里面的project下面的hiberante.properties的。测试的时候,直接运行HibernateTest02即可。于是,comment表就在MySQL数据库中自动创建了。
hiberante.properties的Miscellaneous Settings模块里,有这样几个配置:
hibernate.hbm2ddl.auto create-drop——先删除后在创建
hibernate.hbm2ddl.auto create——直接创建
hibernate.hbm2ddl.auto update——如果有,就更新;没有就创建
6、自动打印SQL语句
你知道么?Hibernate可以查看底层的SQL语句咧!
我们先删掉之前自动生成的表comment;在执行测试HibernateTest02;结果就会显示创建表的语句及查询语句
在hibernate.cfg.xml中增加如下配置:
<!--打印SQL语句-->
<property name="hibernate.show_sql">true</property>
打印输出结果:
INFO: HHH000262: Table not found: t_comment
十月 24, 2017 9:37:56 上午 org.hibernate.tool.schema.extract.internal.InformationExtractorJdbcDatabaseMetaDataImpl processGetTableResults
INFO: HHH000262: Table not found: t_comment
Hibernate: create table t_comment (id integer not null, content varchar(255), com_name varchar(255), primary key (id))
十月 24, 2017 9:37:56 上午 org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select news0_.id as id1_1_, news0_.author as author2_1_, news0_.pic as pic3_1_, news0_.title as title4_1_ from t_news news0_
总条数:509
这里打印输出的语句虽然和我们写的有点区别,但是粘贴后可以执行的。
结论:
1、Hibernate底层跟我们之前写的一样,还是写的SQL,而且还改变了下SQL,所以底层执行效率低;
2、难度由代码转化到配置上,配置简单。
三、HibernateAPI操作数据库
1、未简化之前的CURD代码:
非常简单,查在之前已经测试了,增加、修改、删除只需要增加这几个方法:save、Update、delete即可。
HibernateTest02
package com.hmc.hibernate;
import com.hmc.hibernate.model.Comment;
import com.hmc.hibernate.model.News;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.Test;
import java.util.List;
/**
* User:Meice
* 2017/10/24
*/
public class HibernateTest02 {
//查
@Test
public void testList() {
//注册服务
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
//获取sessionFactory
SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
//查询t_news表中的数据
List<News> list = session.createQuery(" from News").list();//等价于select * from t_news;
System.out.println("总条数:"+list.size());
//关闭session
session.close();
}
//增
@Test
public void testAdd() {
//注册服务
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
//获取sessionFactory
SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
// =========================================================
//实例化对象
Comment comt = new Comment(2,"Cemei","Hibernate is so easy too!");
//开启事务
// session.beginTransaction();
//保存
session.save(comt);
//提交
// session.getTransaction().commit();
//关闭资源
session.close();
}
//改
@Test
public void testUpdate() {
//注册服务
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
//获取sessionFactory
SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
// =========================================================
Comment comt = new Comment(1,"So Hard!","Meice");
session.beginTransaction();
session.update(comt);
session.getTransaction().commit();
//关闭资源
session.close();
}
//删
@Test
public void testDel() {
//注册服务
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
//获取sessionFactory
SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
// =========================================================
Comment comt = new Comment();
comt.setId(1);
session.beginTransaction();
session.delete(comt);
session.getTransaction().commit();
//关闭资源
session.close();
}
}
上面代码冗余,类似我们之前简化BaseDao一样,把重复部分封装起来。
2、简化之后的CURD代码:
1)先对重复部分封装
我们创建一个util包,把重复部分封装如下:
HibernateUtil
package com.hmc.hibernate.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
/**
* User:Meice
* 2017/10/24
*/
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
// A SessionFactory is set up once for an application!
static {
//注册服务
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
//获取sessionFactory
sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
}
//定义获取session的方法getSession()
public static Session getSession() {
Session session = sessionFactory.openSession();
return session;
}
//定义关闭session方法closeSession()
public static void closeSession(Session session) {
if(session != null) {
session.close();
}
}
}
这部分是CURD都会重复的部分,封装起来,直接调用。
2)封装后的CURD如下:
package com.hmc.hibernate;
import com.hmc.hibernate.model.Comment;
import com.hmc.hibernate.model.News;
import com.hmc.hibernate.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;
import java.util.List;
/**
* User:Meice
* 2017/10/24
*/
public class HibernateUtilTest {
Session session = null;
//查
@Test
public void testList() {
try {
session = HibernateUtil.getSession();
//获取查询结构,存放集合中
List<News> list = session.createQuery("from Comment").list();
System.out.println(list.get(0));
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭session
HibernateUtil.closeSession(session);
}
}
//增、改
/*
session.saveOrUpdate()方法可以同时完成save或者update操作
如果没有则增加,有则修改,根据Id
*/
@Test
public void testAdd() {
session = HibernateUtil.getSession();
//开启事务
session.beginTransaction();
Comment comt = new Comment(2,"Hibernate is so hard!","Meice");
session.saveOrUpdate(comt);
//提交事务
session.getTransaction().commit();
//关闭事务
HibernateUtil.closeSession(session);
}
//删
@Test
public void del() {
session = HibernateUtil.getSession();
Comment comt = new Comment();
comt.setId(2);
session.beginTransaction();
session.delete(comt);
session.getTransaction().commit();
HibernateUtil.closeSession(session);
}
}
似不似清爽了很多~~~~~~~~~~~~~~~~~爽歪歪!
总结:
1、对比之前我们写的BaseDao、JdbcDao,Hibernate底层全部封装好,我们直接调用方法即可;
2、通盘没有一条SQL语句,所以即便不会SQL也可以写CURD;
3、那自己为什么还写那么多JDBC的CURD?个人认为先苦后甜嘛!如果不了解底层的实现,直接用框架,如同一个人的举止谈吐,明明没有丰富的阅历,却还满口人生大道理,就显得缺乏内涵。
3、get与load的区别
获取单个对象的时候,有两种方法:get()和load(),二者有何区别?
区别就在于load()方法是lazy的,只有用的时候才会加载——延迟加载。
HibernateTestLazy
package com.hmc.hibernate;
import com.hmc.hibernate.model.Comment;
import com.hmc.hibernate.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;
/**
* User:Meice
* 2017/10/24
*/
public class HibernateTestLazy {
Session session = null;
/**
* 测试查询单个对象时候,get和Load的区别
*/
//查(单个对象)-get()
@Test
public void testGetById () {
try {
session = HibernateUtil.getSession();
Comment comt = session.get(Comment.class,1);
/**
* 直接访问数据库,没有缓存(没有延迟加载)
* 不管三七二十一,先发一个SQL
*/
System.out.println(comt.getId());
System.out.println(comt.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
//查(单个对象)-load()
@Test
public void testLoadById() {
session = HibernateUtil.getSession();
Comment comt = session.load(Comment.class,1);
// 如果获取id,直接从session中获取
System.out.println(comt.getId());
/**
* 如果获取对象的属性在缓存中没有的时候,在发SQL语句查询
* 只有用的时候在发送SQL,这就是延迟加载,也就是lazy的真实含义
* 和Scala中变量前面修饰符lazy异曲同工
*/
System.out.println(comt.getName());
}
/**
* 上面测试结果如下
* 十月 24, 2017 12:37:42 下午 org.hibernate.dialect.Dialect <init>
*INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
* 1
*Hibernate: select comment0_.id as id1_0_0_, comment0_.content as content2_0_0_, comment0_.com_name as com_name3_0_0_ from t_comment comment0_ where comment0_.id=?
*Hibernate is so easy!
*/
//**************************************************************************
//查(单个对象)-get()
@Test
public void testGetById02 () {
try {
session = HibernateUtil.getSession();
Comment comt = session.get(Comment.class,1);
/**
* 无论是否使用get对象,都会发SQL
*/
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 结果:
* 十月 24, 2017 12:47:16 下午 org.hibernate.dialect.Dialect <init>
* INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
* Hibernate: select comment0_.id as id1_0_0_, comment0_.content as content2_0_0_, comment0_.com_name as com_name3_0_0_ from t_comment comment0_ where comment0_.id=?
*/
//查(单个对象)-load()
@Test
public void testLoadById02() {
session = HibernateUtil.getSession();
//没有用到load对象,就不发SQL
Comment comt = session.load(Comment.class,1);
}
/**
* 结果:INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
*十月 24, 2017 12:46:19 下午 org.hibernate.dialect.Dialect <init>
* INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
*/
/**
* 总结:表面上看,load只有再用的时候,才和数据库交互,节约资源,似乎更好
* 但是load也有缺点。。。。。
*/
//以下测试load的缺点
public Comment getById(int id) {
Session session = null;
try {
session = HibernateUtil.getSession();
/**
* 如果session关闭后,再返回对象,再次使用时会报错
* no session异常
* 解决方案:OpenSessionInView
*/
Comment comt = session.load(Comment.class,1);
return comt;
} catch (Exception e) {
e.printStackTrace();
}finally {
HibernateUtil.closeSession(session);
}
return null;
}
@Test
public void testLoad() {
Comment comt = getById(1);
System.out.println(comt);
}
/**
* org.hibernate.LazyInitializationException:
* could not initialize proxy - no Session
*
*/
}
所以语言很多地方是相通的。Scala中修饰变量的关键字lazy,不也和这个有异曲同工之妙么?
四、总结
读者看到这里,一定会累了,嘻嘻…..
1、Hibernate的环境搭建,参考官方文档,soEasy!
2、Hibernate确实很简单,不会SQL也可以写CURD;人人都会写了,程序员肿么办……
3、记住这些关键词,也就记住了Hibernate:
配置方面:property、mapping、DDL
API方面:registry、sessionFactory、session、createQuery、saveOrUpdate、delete
4、lazy的含义——延迟加载。用到的时候,在与后台交互,不用就去睡大觉去。好处是节省资源,坏处方法调用时,容易报no session的异常。
哈哈,结束啦。晚安!
本文详细介绍Hibernate框架的环境搭建过程,包括Maven项目配置、核心API使用方法,并通过具体示例演示了如何利用Hibernate进行数据库操作。
2939

被折叠的 条评论
为什么被折叠?



