之前过了一遍依赖注入的内容,这次过一下数据相关的部分,完成了这部分内容,下篇就涉及到 API 实现了
操作的部分放到下一篇,本篇主要是概念+配置
整体课程上来说,已经完成了将近 1/3,年底之前应该说可以把 spring boot 相关的内容滚完
在开始用 jpa/hibernate 之前,先开始解释一些名词
MVC
MVC 和 spring 没有直接的关联,这是一个更加广泛的,关于 Model-View-Controller 设计模式,其中:
Model
代表数据和业务逻辑相关内容,简单的说就是负责和数据库交流,并且进行数据更新的部分
View
渲染层,直接和用户进行互动的部分,包括从后台获取数据,在前台和用户互动,并且获取用户输入,传输到后台
Controller
负责沟通 model 和 view 的桥梁,将从 view 接收到的数据传给 model 进行数据更新,将从 model 获取的数据传给前台进行渲染
一些比较流行的 MVC 框架有 Ruby,Django(python),Spring MVC(java)express(js),ASP .NET MVC(C#)
SSH
Struts-Spring-Hibernate 框架的缩写,其中 Struts 代表着 View 结构,Spring 是负责 Controller+Model,而 Hibernate 则负责协助和数据库沟通,也是 Model 的一部分
SSM
即 Spring+Spring MVC+Mybatis
这里 Spring 也是负责 Controller+Model 部分,Spring MVC 负责渲染,而 Mybatis 代替了 Hibernate,负责协助数据库的沟通
ORM
Object-Relationship Mapping
使用该框架可以通过映射对象(Object)与关系型数据库(Relational DB),让开发者免于写 SQL,而是直接对对象进行操作即可
比较流行的 ORM 有:Hibernate(Java),ActiveRecord(Ruby),Django’s ORM(Python),Entity Framework(.Net),Sequelize(JS,Node)
Mybatis
Mybatis 是 SQL 映射框架,学习门槛较低,上手较快,因此在 SSM 框架中占据一席之地
基本上说 Mybatis 可以缩短写开发要写的 SQL,并且 MyBatis 负责与数据库进行沟通,提供比较高的灵活性
使用 MyBatis,开发者还是需要写自己的 SQL 的,它并不是一个 ORM 框架
Hibernate
Hibernate 是一个 ORM 框架,因此可以让开发者只操作 Java 对象,而 Hibernate 负责和数据库进行沟通
简而言之就是,Hibernate 借将对应的 java 对象与数据库进行映射,并实现 SQL 操作
Hibernate 是 Spring Boot 的默认 JPA Provider,它本质上还是调用 JDBC 去实现具体的数据库操作
JPA
Java Persistence API 的缩写,这是 API 规范,Hibernate 就是实现了 JPA 的 ORM 框架
换言之,除了 Hibernate 之外,还有其他的框架也实现了 JPA,如 EclipseLink
⚠️:JPA 好像已经改名成了 Jakarta Persistence API
Spring JPA
提供了 boilerplate code 用来加速 JDBC 的操作
使用 Spring JPA 必须要有一个 JPA provider,比较流行的 provider 就是 Hibernate
没有 Provider,Spring JPA 无法实现和数据库的沟通
这样大概就能解释一点 JPA、Spring JPA 和 Hibernate 之间的关系了……?
主要就是额外加了两个 dependencies:
配置数据库这块主要是从 application.properties
里面配置,我配置的内容如下:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/student_tracker
spring.datasource.username=springstudent
spring.datasource.password=springstudent
# deprecated
#spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
# use org.hibernate.dialect.MySQLDialect instead
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
这个配置还是比较简单的,运行结果如下:
补充一下: 我创建了对应的用户和密码,没有创建的话,使用默认的用户名和密码 root
也可以
虽然这个步骤很快,但是实际上我在这一步卡了半天,主要碰到下面几个问题:
缺少 dialect
教程里给的配置没有 spring.jpa.database
,后面 Google 了一下加了 MySQL8Dialect
typo
应该是 org.hibernate.dialect.MySQL8Dialect
,我漏了个 org
数据库不 match
教程里用的是 MySQL,我本地的数据库是 MariaDB v11
后面我试着切换 MariaDB 106 的 Dialect,然后发现 POM 又要更新,折腾若干次未果,后面还是重新下载了一个 MySQL 才跑通
所以说,数据库的版本还是要和 Hibernate 提供的 Dialect 符合,我怀疑 MariaDB11 和 106 不兼容(因为有个大版本的差别)
本地重装数据库冲突
MariaDB 曾经是 MySQL 的套壳,不过现在两个差别越来越大了……
brew 卸载 maria 的话还是删的比较干净的,不过删 MySQL 的时候有个残存的文件夹,这样我重新下载其他的 DB(Maria/MySQL)就会启动失败,解决方案是彻底删除残留的文件夹,再重新安装就好了
我用的脚本为:
❯ brew uninstall mysql
❯ rm -Rf /usr/local/var/mysql
❯ brew install mysql
❯ mysql.server start
配置完成了后,spring boot 就会根据配置自动创建对应的 DataSource
, EntityManager
等数据库相关的 bean
因为 CRUD 的操作比较麻烦,所以教程里面使用的是 CLI,具体内容如下:
package com.example.hibernatejpa;
import com.example.hibernatejpa.dao.StudentDAO;
import com.example.hibernatejpa.entity.Student;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class HibernateJpaApplication {
public static void main(String[] args) {
SpringApplication.run(HibernateJpaApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(StudentDAO studentDAO) {
return runner -> System.out.println("Hello World");
}
}
commandLineRunner
主要是在 Spring 启动后会执行一些命令行输出语句,比如说这里就 log 了一下 Hello World
:
这里也可以修改一下 properties 文件,减少一点 log 的输出:
spring.main.banner-mode=off
logging.level.root=warn
修改了之后,info
相关的内容和 Spring 的 banner 就不会被输出了:
一些本篇笔记会用到的注解如下:
Entity Mapping
@Entity
, @Table
Primary Key
@Id
, @GeneratedValue
Columnh Mapping
@Column
Relationship
这个案例里暂时不会用到,不过是这种关系:@OneToOne
等
Query
@NamedQuery
来自 Spring
@Transactional
代表这会对数据进行 mutation,这里面具体操作还挺复杂的,涉及到 rollback 等操作,之后看看课程里会不会讲,不会的话我抽空研究一下
这种很多,不过用到了再了解就是了
一些类,如 EntityManager
, DataSource
,Spring 会根据配置自动生成,后面直接拿来用就好了
操作流程如下:
定义 Entity Object
JPA 会将这个类与数据库中的 entity 进行 mapping,这也是为什么会有 @Entity
这个注解的来源
package com.example.hibernatejpa.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "student")
public class Student {
}
@Table
指代的则是数据库里 student
这个表
进行数据映射
@Entity
@Table(name = "student")
public class Student {
// defined fields
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
public Student() {
}
public Student(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}
这里需要一个无参构造函数让 hibernate 去使用反射创建对应的实例
这里将表格里有的数据和对象里的属性进行映射,毕竟数据库的结构如下:
@Column
是可选的,如果不提供的话数据库中的名字与 Java 中属性名相同。不过数 DB 的属性名称一般用的是 snake,java 用的是 camel,所以也不太推荐这么做
另外就是,如果代码要更新(比如说换名称之类的),如果不注意这里没修改的话,有可能项目就跑不了了
同样,@Table
这个注解也是可选的
这里从数据库 map 的主键就是 id,@GeneratedValue
也就是主键的生成方式为数据库管理,所以在创建新对象的时候不需要将主键传过去
GenerationType
是可以 override 的,通过实现 IdentifierGenerator
重写对应的方法即可
到这一步,ORM 的 mapping 这里就实现了
接下来就是重写 setter/getter/toString 方法,这里就跳过了
除了手动操作之外,也可以使用 lombok 去除重复的 setter/getter/无参构造函数
比较流行的一些 DB 的 Dialect
RDBMS | Dialects |
---|---|
DB2 | org.hibernate.dialect.DB2Dialect |
DB2 AS/400 | org.hibernate.dialect.DB2400Dialect |
DB2 OS390 | org.hibernate.dialect.DB2390Dialect |
PostgreSQL | org.hibernate.dialect.PostgreSQLDialect |
MySQL5 | org.hibernate.dialect.MySQL5Dialect |
MySQL5 with InnoDB | org.hibernate.dialect.MySQL5InnoDBDialect |
MySQL with MyISAM | org.hibernate.dialect.MySQLMyISAMDialect |
Oracle (any version) | org.hibernate.dialect.OracleDialect |
Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
Sybase | org.hibernate.dialect.SybaseASE15Dialect |
Microsoft SQL Server 2000 | org.hibernate.dialect.SQLServerDialect |
Microsoft SQL Server 2008 | org.hibernate.dialect.SQLServer2008Dialect |
SAP DB | org.hibernate.dialect.SAPDBDialect |
Informix | org.hibernate.dialect.InformixDialect |
HypersonicSQL | org.hibernate.dialect.HSQLDialect |
H2 Database | org.hibernate.dialect.H2Dialect |
Ingres | org.hibernate.dialect.IngresDialect |
Progress | org.hibernate.dialect.ProgressDialect |
Mckoi SQL | org.hibernate.dialect.MckoiDialect |
Interbase | org.hibernate.dialect.InterbaseDialect |
Pointbase | org.hibernate.dialect.PointbaseDialect |
FrontBase | org.hibernate.dialect.FrontbaseDialect |
Firebird | org.hibernate.dialect.FirebirdDialect |
不是非常 up-to-date,MySQL8Dialect 都 deprecated 了,我现在用的 MySQL 版本是 8,不过做一个参考还行
Mysql start up issues | ERROR! The server quit without updating PID file