SpringBoot 集成 Hibernate

发布于:2024-09-05 ⋅ 阅读:(15) ⋅ 点赞:(0)

前言

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

网站公告

今日签到

点亮在社区的每一天
去签到