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

来源:互联网 发布:学科竞赛本科状态数据 编辑:程序博客网 时间:2024/05/21 11:10

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 */@Repositorypublic 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 */@Repositorypublic 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 */@Repositorypublic 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());        }    }}
运行上面的测试方法,就可以对数据库进行操作了。代码托管在码云,可以下载