SpringBoot之通过JPA使用MySQL

291 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

使用数据库是开发基本应用的基础,借助于开发框架我们已经不用编写原始的访问数据库的代码,也不用调用JDBC(Java Data Base Connectivity)或者连接池等诸如此类的被称作底层的代码,我们将在高级的层次上访问数据库。而Spring Boot更是突破了以前所有开发框架访问数据库的方法,在前所未有的更加高级的层次上访问数据库。因为Spring Boot饮食一个功能强大的资源库,为使用Spring Boot的开发者提供了更加简便的接口进行访问。

本文使用JPA(Java Persistence API)资源库来实现对MySQL数据库的操作。简单来说,JPA就是为POJO (Plain Ordinary Java Object)提供持久化的标准规范,即将Java的普通对象通过对象关系映射(Object-RElational Mapping, ORM)持久化到数据库中。

1、配置依赖

创建一个Spring Boot项目,并添加如下依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

2、实体建模

首先创建一些普通对象,用来与数据库的表建立映射关系,我们举RBAC来演示操作数据库,ER模型如下:

image.png

创建实体类:

Department.java

@Data
@Entity
@Table(name = "department")
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}

Role.java

@Data
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}

User.java

@Data
@Entity
@Table(name = "user")
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime registerDate;

    /**
     * @JsonBackReference 用来防止关系对象的递归访问
     */
    @ManyToOne
    @JoinColumn(name = "department_id")
    @JsonBackReference
    private Department department;

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

3、实体持久化

通过实体的定义,实现了使用Java的普通对象(POJO)与数据库表建立映射关系(ORM),接下来使用JPA来实现持久化。

用户实体使用JPA进行持久化,它是一个接口,并继承于JPA资源库JpaRepository接口,使用注解@Repository将这个接口也定义为一个资源库,使它能被其他程序引用,并为其他程序提供存取数据库的功能。

public interface DepartmentRepository extends JpaRepository<Department, Long> {
}

public interface RoleRepository extends JpaRepository<Role, Long> {
}

public interface UserRepository extends JpaRepository<User, Long> {
}


image.png

上图是JapRepository接口继承首张,JapRepository继承于PagingAndSortingRepository,它提供了分页和排序的功能,PagingAndSortingRepository继承于CrudRepository,它提供了简单的增删查改的功能。因为定义的接口继承于JpaRepository,所以它传递性地继承了上面所有这些接口,并拥有这些接口的所有方法。这些接口提供的一些方法如下:

  • <S extends T> S save(S entity);
  • <S extends T> Iterable<S> saveAll(Iterable<S> entities);
  • Optional<T> findById(ID id);
  • boolean existsById(ID id);
  • Iterable<T> findAll();
  • Iterable<T> findAllById(Iterable<ID> ids);
  • long count();
  • void deleteById(ID id);
  • void delete(T entity);
  • void deleteAllById(Iterable<? extends ID> ids);
  • void deleteAll(Iterable<? extends T> entities);
  • void deleteAll();
  • List<T> findAll();
  • List<T> findAll(Sort sort);
  • List<T> findAllById(Iterable<ID> ids);
  • <S extends T> List<S> saveAll(Iterable<S> entities);
  • void flush();
  • <S extends T> S saveAndFlush(S entity);
  • <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
  • void deleteAllInBatch(Iterable<T> entities);
  • void deleteAllByIdInBatch(Iterable<ID> ids);
  • void deleteAllInBatch();
  • T getReferenceById(ID id);
  • <S extends T> List<S> findAll(Example<S> example);
  • <S extends T> List<S> findAll(Example<S> example, Sort sort);

4、MySQL测试

首先,增加一个使用JPA的配置类,下面我们定义一个测试时使用的JPA配置类:

@Configuration
@EnableJpaRepositories(basePackages = "com.**.repository")
public class JpaConfig {

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

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

        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan("com.**.entity");
        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());
    }
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JpaConfig.class})
public class MySqlTest {
    private static Logger log = LoggerFactory.getLogger(MySqlTest.class);

    @Autowired
    UserRepository userRepository;

    @Autowired
    DepartmentRepository departmentRepository;

    @Autowired
    RoleRepository roleRepository;

    @Before
    public void initData() {
        userRepository.deleteAll();
        roleRepository.deleteAll();
        departmentRepository.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.setRegisterDate(LocalDateTime.now());
        user.setDepartment(department);
        List<Role> roles = roleRepository.findAll();
        Assert.notNull(roles);
        user.setRoles(roles);
        userRepository.save(user);
        Assert.notNull(user.getId());
    }

    @Test
    public void test() {
        System.out.println("hello world");
    }

    @Test
    public void testFindPage() {
        Pageable pageable = PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "id"));
        Page<User> page = userRepository.findAll(pageable);
        Assert.notNull(page);
        System.out.println("size: " + page.getSize());
        for (User user : page.getContent()) {
            System.out.println("11111");
            log.info("===user=== user name:{}, department name: {}, role name: {}",
                    user.getName(), user.getDepartment().getName(), user.getRoles().get(0).getName());
        }
    }
}