大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: JDBC&Hibernate 修改讲义

Hibernate简介

为什么是她?

Java ORM一个字形容:乱;两个字:很乱;三个字:非常乱。

但马马虎虎勉勉强强可以选两大巨头:Hibernate和myBatis(如果myBatis也算是ORM的话

选择Hibernate教学,主要是因为她:

  • 功能齐全:所以理论上你会了Hibernate,再学/用其他的ORM,难度最小
  • 真正面向对象:针对entity(而不是针对表)进行查询……,要在学习中体会。mybatis的崛起,其实是对“面向对象”的一种反思和批判

历史

2001年11月:27岁(英雄出少年啊!)澳大利亚程序员Gavin King,发布Hibernate 1.0版本,免费开源,历史悠久。

2003年9月:Hibernate开发团队加入JBoss公司,开始全职开发。

2006年:JPA(Java Persistent API,持久化框架标准)发布,Hibernate在3.2版本开始,已经完全兼容JPA标准。

PS:因为JPA最主流最强大的实现就是Hibernate,所以经常看到有人把JPA和Hibernate混为一谈。其实实现JPA标准的还有:EclipseTop、OpenJPA、Spring Data JPA……

课程说明

版本选择:5.1,略新,2018年底发布final版(实际开发中怕是3.0版本的多,但飞哥也要考虑收视率……)

尽量简洁:如无必要,ABC三种方案都可以实现,就只讲最主流最方便的一种(尽量以JPA为准,此外还有hibernate保留的独有的native),不铺开了……


maven配置

在已有JDBC所需的jar包(比如mysql-connector)基础上,下载hibernate相关依赖:
<dependency>
    <groupId>org.hibernate</groupId>
    <!-- 因为要使用JPA模式,所以是entitymanager;否则Java-8 -->
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.1.17.Final</version>
</dependency>
核心jar包:
  • hibernate-core
  • hibernate-jpa-2.1-api
  • hibernate-entitymanager
  • hibernate-commons-annotations
  • jboss-logging
  • ……


准备entity:Student

按照JPA的要求(native的要求略弱)准备了Student类:

  • 必须有一个公共无参构造函数,因为hibernate需要利用反射由此构建entity
  • 按Java Bean的要求定义字段和属性(getter/setter)
  • 能够继承,没有fianal修饰(便于proxy lazy load)
  • ……
public class Student {
	private int id;
	private String name;

	public int getId() {
		return id;
	}

	public void setId(int id) {

annotation和xml

这样和普通Java类没有区别(比如没有要求必须继承自某个基类)的entity又被称之为POJO(Plain Old Java Object,简单的Java对象)。

PS:怎么才不普通?有些ORM要求所有的entity必须继承自某个基类啥的……

由于entity都是POJO,我们还需要告诉hibernate哪些类是需要映射的entity。可以选择:

  1. 使用.xml文件,详见文档(复习XML格式)
  2. 在类上添加annotation说明,我们的课程使用这一种方式
    import javax.persistence.Entity;
    
    @Entity    //标明这个类是entity
    public class Student {
        @Id    //JPA要求entity必须有一个主键   
        private int id;


META-INF/persistence.xml

按JPA规范要求,接下来需要一个persistence.xml文件,进行一些配置,告诉hibernate如何连接数据库等信息……

PS:如果是native模式的话,需要hibernate.cfg.xml文件

class path

你在读文档的时候会看到classpath,比如:

(Hibernate in this case) is required to locate all JPA configuration files by classpath lookup of the META-INF/persistence.xml resource name.

classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索java类文件。

不同的项目有不同的classpath(还可以自定义设定)

演示:eclipse中查看全局/项目的class path

persistence.xml内容

eclipse演示:新建一个xml文件

以下部分只需要复制粘贴即可:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
	version="2.1">
</persistence>
persistence下面可以有若干个persistence-unit(持久化单位)
	<!-- name自定义,后面有用 -->
	<persistence-unit name="mysql-jpa">
	</persistence-unit>
实际开发中可能连接多个/种数据库,每个/种连接都可以定义一个persistence-unit。

persistence-unit下首先要定义一个provider(详见后文):

		<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

然后是若干properties,首先是和JDBC连接字符串对应的连接信息:

		<properties>
			<property name="javax.persistence.jdbc.driver"
				value="com.mysql.jdbc.Driver" />
			<property name="javax.persistence.jdbc.url"
				value="jdbc:mysql://localhost:3306/17bang" />
			<property name="javax.persistence.jdbc.user" value="root" />
			<property name="javax.persistence.jdbc.password" value="" />
		</properties>


EntityManagerFactory

首先导入:

import javax.persistence.*;

JPA也使用的工厂模式

  • 首先创建一个工厂。创建一个工厂是耗时的,所以用static
    private static EntityManagerFactory entityManagerFactory;
    static {
        entityManagerFactory = Persistence.createEntityManagerFactory(
                "mysql-jpa" /* persistence-unit中定义的名字! */
                );
    }

演示运行:正确配置,有log生成(persistence-unit/driver/connection pool/ dialect等


schema-generation

可以在persistence-unit中添加一个property:

<property
	name="javax.persistence.schema-generation.database.action"
	value="drop-and-create" />

这样Hibernate在生成EntityManagerFactory的时候,根据entity映射配置和它的value值,自动的:

  • 创建表:create
  • 删除表:delete
  • 先删除再创建表:drop-and-create
  • 更新表:update

这些操作都是通过生成SQL语句完成的。

有时候(比如生产环境部署)我们还需要拿到这些SQL语句,这时候我们就需要额外的配置:

<property
	name="javax.persistence.schema-generation.scripts.action"
	value="drop-and-create" />
value值指示何时生成SQL脚本。

生成的SQL脚本会保存到文件中,所以,还需要指定文件路径:

<property
	name="javax.persistence.schema-generation.scripts.drop-target"
	value="sqlscript/drop.sql" />
<property
	name="javax.persistence.schema-generation.scripts.create-target"
	value="sqlscript/create.sql" />

删除表的SQL(drop-target)和更新表(create-target,包含create和update)的SQL脚本需要分开存放。

默认生成的SQL语句没有换行和缩进,以下配置可以格式化的输出SQL语句:
            <property name="hibernate.format_sql" value="true"/>


provider模式

F3和断点演示:createEntityManagerFactory()中调用getProviders()

实际上通过配置文件获得了类:

public class HibernatePersistenceProvider implements PersistenceProvider {

这个类实现了PersistenceProvider接口,用于构建entityManagerFactory(复习:session factory)

#理解:通过provider模式(仍然是API和实现分离),可以利用配置在不同的provider间灵活的切换


自定义映射

给Student继续添加字段:
private boolean isMale;    //bit
private float score;    //float
private int age;    //integer

private DayOfWeek rest;		//integer
private String description;	//varchar(255)
private UUID uid;	//binary

演示:默认映射效果

很多时候,hibernate默认的映射不能完全满足我们的需求,所以还需要自定义的映射。

我们仍然采用annotation的方式,目前两种annotation都可以使用:

import javax.persistence.*;    //以persistence为主,所以*概括
import org.hibernate.annotations.Check;    //hibernate为辅,所以要指明具体类型
import org.hibernate.annotations.Nationalized;

注解字段

自动生成:

  • 自增
    @GeneratedValue( strategy = GenerationType.IDENTITY )
    //id integer not null auto_increment,
    
  • UUID:但不会影响schema生成,因为UUID会由Java生成(仅当其为主键时
    @GeneratedValue
    private UUID id;
    //uid binary(255),
    

改列名:

@Column(name = "sname")
//sname varchar(255),

可空:(默认都是不可空的)

@Column(nullable = true)
//isMale bit,

设置默认值(传入的始终是字符串)

@ColumnDefault("18")
//age integer default 18 not null,

设定文本/字符串的

  • 长度为50:
    @Column(length = 50)
    //sname varchar(50),
    
  • 使用unicode,即:nvarchar而不是varchar
    @Nationalized
    //sname nvarchar(50),
    
  • 不限长度的字符串(Large Object,大对象):
    @Lob
    //description longtext,
    

设定小数位数(必须使用BigDecimal):

@Column(precision = 5, scale = 2)		
private BigDecimal score;
//score decimal(5,2),

PS:@Column里面还可以有多个并列的描述。

添加唯一约束:

@Column(unique = true)

但这样生成的unique约束名是mysql的“乱码”名(实际上是根据表名列名等编码所得)

alter table Student 
    add constraint UK_tlswl9chftpfrcq9o86kys6ug unique (sname)

如果要使用自定义的名称,要在注解类的@Table中声明

关于时间

Java8以前的Date和Calendar,默认都会映射成datetime:
private Date enroll;
private Calendar created;
如果我们能确定只使用日期,可以添加一个:
@Temporal(TemporalType.DATE)
//enroll date,

能够稍微节省一点空间。

但对于Java8引入的LocalDateTime、LocalDate和LocalTime,需要maven引入

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-java8</artifactId>
	<version>5.1.17.Final</version>
</dependency>
才能映射成datetime、date或time,¯\_(ツ)_/¯
private LocalDate enroll;
//enroll date,
private LocalDateTime created;
//created datetime,
否则会映射成tinyblob类型,哈哈……

注解类

改表名:

@Table(name = "T_Student")
自定义检查:(注意要标记在字段上!)
@Check(constraints = "age>0")

唯一约束

@Table(uniqueConstraints = { 
	//columnNames是表上真实的列名
	@UniqueConstraint(name = "UK_Name", columnNames = { "sname" }) }
)

如果要定义约束名称的话,只能这样。还可以多列组合约束

新建索引:
@Table(indexes = { 
    @Index(name = "IX_Name", columnList = "name", unique = true) }

注意这些annotation只是确定表结构的生成,不校验/影响entity的生成。比如@ColumnDefault("18")并不会在我new一个Student()的时候给age自动赋值成18.


作业

  1. 完成ORM介绍中
    1. 第1题
    2. 第2题,要能拿到修改表结构的script
  2. 试试用native的xml方式,完成上述作业。参考:Native BootstrappingUsing Native Hibernate APIs and hbm.xml Mapping
  3. 了解mybatis的启动
学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 JDBC&Hibernate 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码