专栏首页深入理解JavaSpring Boot+JPA+Mysql完成数据库整合操作

Spring Boot+JPA+Mysql完成数据库整合操作

Spring Boot结合JPA操作Mysql数据库十分方便,可以做到零配置文件。具体流程如下。

一、Maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>springboot.example</groupId>
    <artifactId>springboot-mysql</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- springboot操作数据库依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- mysql连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

二、实体类

本次测试一共设置了三个实体类,分别是User(用户)、Role(角色)、Department(部门)。三者之间的关系是:一个用户可以拥有多个角色,也就是用户和角色之间是一对多的关系,一个用户只属于一个部门,一个部门可以拥有多个用户,也就是说用户和部门之间是多对一的关系。

User.java

package com.lemon.springboot.domain;

import com.fasterxml.jackson.annotation.JsonBackReference;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * @author lemon
 */
@Entity
@Table(name = "user")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", length = 20)
    private String name;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Column(name = "createTime")
    private Date createDate;

    @ManyToOne
    @JoinColumn(name = "did")
    // 防止递归访问
    @JsonBackReference
    private Department department;

    @ManyToMany(cascade = {}, fetch = FetchType.EAGER)
    @JoinTable(name = "user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private List<Role> roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", createDate=" + createDate +
                ", department=" + department +
                ", roles=" + roles +
                '}';
    }
}

Role.java

package com.lemon.springboot.domain;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author lemon
 */
@Entity
@Table(name = "role")
public class Role implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", length = 20)
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Department.java

package com.lemon.springboot.domain;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author lemon
 */
@Entity
@Table(name = "department")
public class Department implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "name", length = 20)
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

三、DAO层

DAO层采用的是Spring Data JPA,这个操作数据库十分方便。

  • Repository (空接口)
  • CrudRepository (增删改查)
  • PagingAndSortingRepository (分页和排序)
  • JpaRepository (扩展增删改查、批量操作 )
  • JpaSpecificationExecutor: 用来做负责查询的接口
  • Specification:是Spring Data JPA提供的一个查询规范, 要做复杂的查询,类似hibernate QBC查询

Spring Data JPA的使用十分简单,只需要我们编写DAO接口来继承上述的接口即可,不需要编写这个接口的实现类,然后我们在Service层注入编写的接口即可。在这里我们一般都是直接继承JpaRepository这个接口。因为上述的前四个接口存在着一层一层的继承关系,我们的接口继承了JpaRepository,也就具备了它的父接口所有的方法。

如果持久层接口较多,且每一个接口都需要声明相似的增删改查方法,直接继承 Repository 就显得有些啰嗦,这时可以继承 CrudRepository,它会自动为域对象创建增删改查方法,供业务层直接使用。开发者只是多写了 "Crud" 四个字母,即刻便为域对象提供了开箱即用的十个增删改查方法。 但是,使用 CrudRepository 也有副作用,它可能暴露了你不希望暴露给业务层的方法。比如某些接口你只希望提供增加的操作而不希望提供删除的方法。针对这种情况,开发者只能退回到 Repository 接口,然后到 CrudRepository 中把希望保留的方法声明复制到自定义的接口中即可(体现了强大的灵活性)。 分页查询和排序是持久层常用的功能,Spring Data 为此提供了 PagingAndSortingRepository 接口,它继承自 CrudRepository 接口,在 CrudRepository 基础上新增了两个与分页有关的方法。但是,我们很少会将自定义的持久层接口直接继承自 PagingAndSortingRepository,而是在继承 Repository 或 CrudRepository 的基础上,在自己声明的方法参数列表最后增加一个 Pageable 或 Sort 类型的参数,用于指定分页或排序信息即可,这比直接使用 PagingAndSortingRepository 提供了更大的灵活性。 JpaRepository 是继承自 PagingAndSortingRepository 的针对 JPA 技术提供的接口,它在父接口的基础上,提供了其他一些方法,比如 flush(),saveAndFlush(),deleteInBatch() 等。如果有这样的需求,则可以继承该接口。 上述四个接口,开发者到底该如何选择?其实依据很简单,根据具体的业务需求,选择其中之一。下面写出三个Repository。

UserRepository.java

package com.lemon.springboot.repository;

import com.lemon.springboot.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * @author lemon
 */
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}

RoleRepository.java

package com.lemon.springboot.repository;

import com.lemon.springboot.domain.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * @author lemon
 */
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
}

DepartmentRepository.java

package com.lemon.springboot.repository;

import com.lemon.springboot.domain.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * @author lemon
 */
@Repository
public interface DepartmentRepository extends JpaRepository<Department, Long> {

}

四、JPA配置类(重要)

这个配置类代替了传统的配置文件模式,配置信息都写在这个配置类中。

package com.lemon.springboot.configuration;

import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * JPA配置类
 *
 * @author lemon
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@EnableJpaRepositories(basePackages = "com.lemon.springboot.repository")
@EntityScan(basePackages = "com.lemon.springboot.domain")
public class JpaConfiguration {

    @Bean
    PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://192.168.25.133:3306/springboot?characterEncoding=utf8");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan("com.lemon.springboot.domain");
        entityManagerFactoryBean.setJpaProperties(buildHibernateProperties());
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter() {{
            setDatabase(Database.MYSQL);
        }});
        return entityManagerFactoryBean;
    }

    protected Properties buildHibernateProperties() {
        Properties hibernateProperties = new Properties();

        hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        hibernateProperties.setProperty("hibernate.show_sql", "true");
        hibernateProperties.setProperty("hibernate.use_sql_comments", "false");
        hibernateProperties.setProperty("hibernate.format_sql", "true");
        hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "update");
        hibernateProperties.setProperty("hibernate.generate_statistics", "false");
        hibernateProperties.setProperty("javax.persistence.validation.mode", "none");

        //Audit History flags
        hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", "true");
        hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", "true");

        return hibernateProperties;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager();
    }

    @Bean
    public TransactionTemplate transactionTemplate() {
        return new TransactionTemplate(transactionManager());
    }
}

五、测试

package com.lemon.springboot.test;

import com.lemon.springboot.configuration.JpaConfiguration;
import com.lemon.springboot.domain.Department;
import com.lemon.springboot.domain.Role;
import com.lemon.springboot.domain.User;
import com.lemon.springboot.repository.DepartmentRepository;
import com.lemon.springboot.repository.RoleRepository;
import com.lemon.springboot.repository.UserRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JpaConfiguration.class})
public class SpringBootMysqlTest {

    private static Logger logger = LoggerFactory.getLogger(SpringBootMysqlTest.class);

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private DepartmentRepository departmentRepository;
    @Autowired
    private RoleRepository roleRepository;

    /**
     * 初始化数据
     */
    @Before
    public void init() {
        // 删除数据库所有数据
        userRepository.deleteAll();
        departmentRepository.deleteAll();
        roleRepository.deleteAll();

        // 向部门添加数据
        Department department = new Department();
        department.setName("开发部");
        departmentRepository.save(department);
        Assert.notNull(department.getId(), "主键生成失败");

        // 向角色添加数据
        Role role = new Role();
        role.setName("admin");
        roleRepository.save(role);
        Assert.notNull(role.getId(), "主键生成失败");

        // 向用户添加数据
        User user = new User();
        user.setName("user");
        user.setCreateDate(new Date());
        user.setDepartment(department);
        List<Role> roles = roleRepository.findAll();
        Assert.notNull(role, "roles为空");
        user.setRoles(roles);
        userRepository.save(user);
        Assert.notNull(user.getId(), "主键生成失败");
    }

    @Test
    public void findPage() {
        Pageable pageData = new PageRequest(0, 10, new Sort(Sort.Direction.ASC, "id"));
        Page<User> page = userRepository.findAll(pageData);
        Assert.notNull(page, "未查询到数据");
        for (User user : page.getContent()) {
            logger.info("=====user===== user name:{}, department name:{}, role name:{}",
                    user.getName(), user.getDepartment().getName(), user.getRoles().get(0).getName());

        }
    }

}

运行上面的测试方法,就可以对数据库进行操作了。代码托管在码云,可以下载

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring Boot整合Redis

    本次测试一共设置了三个实体类,分别是User(用户)、Role(角色)、Department(部门)。三者之间的关系是:一个用户可以拥有多个角色,也就是用户和...

    itlemon
  • Spring Cloud微服务技术栈(五):客户端负载均衡Spring Cloud Ribbon部分源码分析

    为了使客户端具备负载均衡的能力,我们在代码中将RestTemplate交给Spring管理的时候,会加上@LoadBalanced注解,如下代码所示:

    itlemon
  • Spring Boot错误处理

    自定义一个类实现ErrorController,当系统发生404或者500错误的时候,就会自动进入到自定义的错误页面中,这要求在resources文件里面的te...

    itlemon
  • (四)SpringBoot2.0基础篇- 多数据源,JdbcTemplate和JpaRepository

  • Spring框架系列(二)之Bean的注解管理

    微信公众号:compassblog 欢迎关注、转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1、Spring中的两种容器 在系列(一)中我们已经知道...

    compassblog
  • 重温java代理模式

    我是攻城师
  • 由浅入深,讲解 spring 实战详细使用——spring 源码篇

    首先,这篇文章不会详细的深入底层源码,只是基于注解和配置来说说我们的 spring 的使用,别小看基础,保证有你没用过的注解和配置,走起。

    淡定的蜗牛
  • java架构之路-(spring源码篇)由浅入深-spring实战详细使用

      今天我更新了一篇jvm垃圾回收的算法和垃圾回收器的内部逻辑,但是看的人不多啊......貌似大家还是比较喜欢看源码吧,毕竟实战要比理论用的多。

    小菜的不能再菜
  • Java并发之Executor(返回结果处理)运行多个任务并处理第一个结果运行多个任务并处理所有结果

    并发编程常见的问题,就是当采用多个并发任务来解决一个问题,我们往往只对第一个返回的结果有兴趣。比如,对一个数组有多种排序算法,可以并发启动所有算法,但是对于一个...

    desperate633
  • ssh搭建开发环境

    公司一直不是ssh零配置的框架,每次写action都要在applicationcontext和struts里面配置,好麻烦,最近有空,写了一个ssh零配置的框架...

    xiangzhihong

扫码关注云+社区

领取腾讯云代金券