前言
Hibernate是一个对象/关系映射(ORM)解决方案,适用于用Java和其他JVM语言编写的程序。
虽然使用Hibernate不需要很强的SQL背景,但对其概念的基本理解是有用的——尤其是数据建模的原理。了解事务和设计模式(如工作单元)的基础知识也很重要。
Hibernate是一个全自动的ORM框架。ORM框架通过其独特的优势,可以简化开发、提高可维护性、跨数据库支持和提高性能等。为开发者节约大量的时间。
Hibernate 的主要功能
1.对象关系映射(ORM):Hibernate可以自动将Java类和对象映射到关系数据库中的表和行。
2.透明持久性:Hibernate可以自动管理对象的生命周期,并将其持久化到数据库中,从而使开发人员可以专注于业务逻辑,而不必担心对象的状态管理。
3.查询语言:Hibernate提供了一种基于面向对象的查询语言(HQL),使开发人员可以使用面向对象的方式来查询和操作数据库。
4.缓存管理:Hibernate提供了一个高效的缓存管理机制,可以提高应用程序的性能。
5.事务管理:Hibernate可以自动管理数据库事务,从而确保数据的完整性和一致性。
使用Hibernate的好处
1.简化数据访问层:Hibernate简化了数据访问层的开发,开发人员只需要关注业务逻辑,而不必编写繁琐的SQL语句。
2.提高开发效率:Hibernate可以自动生成数据库表和列,省去了手动创建和维护数据库表的麻烦。此外,Hibernate还提供了一种基于对象的查询语言,使得查询和操作数据变得更加简单。
3.提高可维护性:Hibernate使得应用程序的代码更加清晰、简单易懂,使得应用程序更加易于维护。
4.支持多种数据库:Hibernate可以很方便地支持多种关系数据库,包括MySQL、Oracle、Microsoft SQL Server等。
5.提高性能:Hibernate提供了一个缓存机制,可以提高应用程序的性能。此外,Hibernate还支持批量处理和延迟加载等优化技术,可以进一步提高应用程序的性能。
6.支持事务管理:Hibernate可以自动管理数据库事务,从而确保数据的完整性和一致性。开发人员可以专注于业务逻辑,而不必手动处理事务。
开始前
启动MySQL服务,创建demo需要的表。
use demo
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用https://start.spring.io/
初始化springboot项目
我自己比较喜欢的项目结构
---
|-api // 对外接口,controller
|-dto // 对外传输实体
|-domain // 领域
|-entity // 领域对象
|-repository // 仓储,数据存储与获取
|-service // 领域服务
Maven 依赖
Hibernate作为ORM框架,它可以替代JdbcTemplate,但Hibernate仍然需要JDBC驱动,所以,我们需要引入JDBC驱动、连接池,以及Hibernate本身。在Maven中,我们加入以下依赖项:
<properties>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<mysql.version>8.0.28</mysql.version>
<druid.version>1.1.21</druid.version>
<hibernate.version>6.6.0.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
</dependencies>
配置
application.properties
spring.application.name=hibernate-demo
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.connection.autocommit=true
# 显示SQL并格式化SQL
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
jpa 配置
在JpaConfig
中,我们仍然需要引入JDBC配置文件,声明repository的位置,以及启用声明式事务。
为了启用Hibernate,还需要创建一个LocalSessionFactoryBean。
HibernateTransactionManager 是配合Hibernate使用声明式事务所必须的。
package com.zxy.demo.hibernate;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import jakarta.persistence.EntityManagerFactory;
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "dbEntityManagerFactory",
transactionManagerRef = "dbTransactionManager",
basePackages = {"com.zxy.demo.hibernate.domain.repository" }
)
public class JpaConfig {
@Autowired
private DataSource dataSource;
@Autowired
private JpaProperties jpaProperties;
@Bean(name = "dbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
// 设置实体类的包路径
em.setPackagesToScan("com.zxy.demo.hibernate.domain.entity");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// https://github.com/spring-projects/spring-boot/issues/39753
em.setEntityManagerFactoryInterface(EntityManagerFactory.class);
em.setJpaProperties(hibernateProperties());
return em;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
for (Map.Entry<String, String> entry : jpaProperties.getProperties().entrySet()) {
properties.put(entry.getKey(), entry.getValue());
}
return properties;
}
@Bean(name = "dbTransactionManager")
public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory.getObject());
}
}
Entity
对应数据库表对象。
id
是自增主键,
这种映射关系十分易懂,但我们需要添加一些注解来告诉Hibernate如何把User类映射到表记录,
防止表名映射错误,可以追加一个@Table(name="user")
表示,
每个属性到数据库列的映射用@Column()
标识,nullable
指示列是否允许为NULL,updatable
指示该列是否允许被用在UPDATE语句,length
指示String类型的列的长度。
package com.zxy.demo.hibernate.domain.entity;
import java.io.Serializable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
// 配置表的名称
@Entity
@Table(name="user")
public class UserEntity implements Serializable {
// MySQL 等的自增ID,用 strategy = GenerationType.IDENTITY
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = true, updatable = false)
private long id;
@Column(nullable = false, length = 100)
private String name;
// 各个getter与setter
}
Repository 与 Service
Repository
package com.zxy.demo.hibernate.domain.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.zxy.demo.hibernate.domain.entity.UserEntity;
// 继承JpaRepository、JpaSpecificationExecutor,就有了最基本的CRUD操作,很方便是不是
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long>, JpaSpecificationExecutor<UserEntity> {
}
Service
package com.zxy.demo.hibernate.domain.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zxy.demo.hibernate.domain.entity.UserEntity;
import com.zxy.demo.hibernate.domain.repository.UserRepository;
import com.zxy.demo.hibernate.domain.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public long createUser(UserEntity userEntity) {
userEntity = userRepository.saveAndFlush(userEntity);
return userEntity.getId();
}
@Override
public void updateUser(UserEntity userEntity) {
var oldUser = userRepository.findById(userEntity.getId());
if (oldUser.isEmpty()) {
throw new RuntimeException("User not found. id: " + userEntity.getId());
}
var oldUserEntity = oldUser.get();
oldUserEntity.setName(userEntity.getName());
// TODO 其他字段
userRepository.save(oldUserEntity);
}
}
api
package com.zxy.demo.hibernate.api;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zxy.demo.hibernate.domain.entity.UserEntity;
import com.zxy.demo.hibernate.domain.repository.UserRepository;
import com.zxy.demo.hibernate.domain.service.UserService;
import com.zxy.demo.hibernate.dto.User;
@Controller
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class UserController {
// get跟list比较简单,就不封装Service接口了,直接Controller中使用
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
private UserEntity toUserEntity(User u) {
if (null == u) {
return null;
}
UserEntity userEntity = new UserEntity();
userEntity.setId(u.id);
userEntity.setName(u.name);
return userEntity;
}
private User toUser(UserEntity userEntity) {
if (null == userEntity) {
return null;
}
User user = new User();
user.id = userEntity.getId();
user.name = userEntity.getName();
return user;
}
@ResponseBody
@RequestMapping(value = "/users")
public List<User> users() {
List<UserEntity> users = userRepository.findAll();
return users.stream().map(this::toUser).collect(Collectors.toList());
}
@ResponseBody
@RequestMapping(value = "/users/{id}")
public User getUser(@PathVariable("id") long uid) {
var user = userRepository.findById(uid);
if (user.isPresent()) {
return toUser(user.get());
}
return null;
}
@ResponseBody
@PostMapping(value = "/users")
public User creatUser(@RequestBody User u) {
UserEntity userEntity = toUserEntity(u);
long id = userService.createUser(userEntity);
u.id = id;
return u;
}
@ResponseBody
@PutMapping(value = "/users/{id}")
public User updateUser(@PathVariable("id") long uid, @RequestBody User u) {
u.id = uid;
UserEntity userEntity = toUserEntity(u);
userService.updateUser(userEntity);
return u;
}
}
main
最后的main方法入口就很简单了。
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
资料
- https://start.spring.io/
- https://docs.jboss.org/hibernate/orm/6.6/quickstart/html_single/
- https://hibernate.net.cn/
- https://github.com/hibernate/hibernate-orm
- https://www.baeldung.com/the-persistence-layer-with-spring-and-jpa