Spring Boot 学习笔记 5 : JPA

时间:2022-01-06 16:07:13
  1. 在 pom.xml 文件中添加依赖:

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    由于使用 HikariCP 连接池,添加下面依赖:

    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
  2. 在 application.yml 文件中添加配置:

    spring:
        datasource:
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false
            username: root
            password:
            type: com.zaxxer.hikari.HikariDataSource
        jpa:
            database: mysql
            database-platform: MYSQL
            show-sql: true
            format-sql: true
            hibernate:
                ddl-auto: update
                naming:
                    strategy: org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
  3. 编写配置类:

    import com.mj.wcs.domain.User;
    import com.mj.wcs.repository.UserRepository;
    import org.springframework.boot.autoconfigure.domain.EntityScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /** * @author smileorsilence * @date 2018/03/28 */
    @Configuration
    @EntityScan(basePackageClasses = User.class)
    @EnableJpaRepositories(basePackageClasses = UserRepository.class)
    @EnableTransactionManagement
    public class HibernateJpaConfiguration {
    
    }
  4. 编写实体类:

    import com.fasterxml.jackson.annotation.JsonIgnore;
    import lombok.Data;
    
    import javax.persistence.*;
    import javax.validation.constraints.NotNull;
    import java.io.Serializable;
    
    /** * @author smileorsilence */
    @Data
    @Entity
    @Table(name = "user")
    public class User implements Serializable {
    
        @Id
        @GeneratedValue
        private Long id;
    
        @NotNull
        @Column(name = "username")
        private String username;
    
        @NotNull
        @JsonIgnore
        @Column(name = "password")
        private String password;
    
        @Column(name = "email", unique = true)
        private String email;
    
    }
  5. 编写Repository类:

    import com.mj.wcs.domain.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    import java.util.Optional;
    
    /** * @author smileorsilence * @date 2018/03/28 */
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
    
        Optional<User> findOneByEmail(String email);
    }
  6. 编写Service类:

    import com.mj.wcs.domain.User;
    import com.mj.wcs.repository.UserRepository;
    import com.mj.wcs.service.util.MD5Util;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.inject.Inject;
    
    /** * @author smileorsilence * @date 2018/03/28 */
    @Slf4j
    @Service
    @Transactional
    public class UserService {
    
        @Inject
        private UserRepository userRepository;
    
        public User saveUser() {
            User user = new User();
            user.setUsername("爱他明月好");
            user.setPassword(MD5Util.encodeByMD5("憔悴也相关"));
            user = userRepository.save(user);
            log.info("saveUser, user : {}", user);
            return user;
        }
    
    }
  7. 编写Controller类:

    import com.mj.wcs.domain.User;
    import com.mj.wcs.service.UserService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.inject.Inject;
    
    /** * @author smileorsilence * @date 2018/03/28 */
    @Api("test")
    @Slf4j
    @Controller
    @RequestMapping("/api")
    public class UserController {
    
        @Inject
        private UserService userService;
    
        @ApiOperation(value = "测试saveUser方法")
        @RequestMapping(value = "/user/save", method = RequestMethod.GET)
        @ResponseBody
        public User saveUser() {
            log.info("saveUser");
            return userService.saveUser();
        }
    }
  8. 运行程序,抛出异常:

    2018-03-28 16:47:37.032 ERROR 1744 --- [           main] o.s.boot.SpringApplication               : Application startup failed
    
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        ... ? common frames omitted
    Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
        at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:264) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
        ... ? common frames omitted
    Caused by: org.hibernate.boot.registry.selector.spi.StrategySelectionException: Unable to resolve name [MYSQL] as strategy [org.hibernate.dialect.Dialect]
        at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.selectStrategyImplementor(StrategySelectorImpl.java:113) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
        ... ? common frames omitted

    定位异常原因是 application.yml 配置文件中 JPA 配置错误:

    spring:
        jpa:
            database-platform: MYSQL

    修改为如下:

    spring:
        jpa:
            database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

    抛出异常:

    2018-03-28 18:10:00.899 ERROR 7904 --- [           main] o.s.boot.SpringApplication               : Application startup failed
    
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
    Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:954) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
        ... ? common frames omitted
    Caused by: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: Unknown database 'nmgyj-wcs'
        at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:512) ~[HikariCP-2.5.1.jar:na]
        ... ? common frames omitted
    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database 'nmgyj-wcs'
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_111]
        ... ? common frames omitted

    定位异常原因是 MySQL 数据库中找不到 nmgyj-wcs 数据库错误:

    创建对应数据库即可。

  9. 使用Postman测试:

    Spring Boot 学习笔记 5 : JPA

  10. 编写Junit单元测试:

    import com.mj.wcs.domain.User;
    import com.mj.wcs.repository.UserRepository;
    import com.mj.wcs.service.util.MD5Util;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import javax.inject.Inject;
    
    /** * @author smileorsilence * @date 2018/03/28 */
    @Slf4j
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest
    public class UserServiceTest {
    
        @Inject
        private UserRepository userRepository;
    
        @Test
        public void saveUser() throws Exception {
            User user = new User();
            user.setUsername("爱他明月好");
            user.setPassword(MD5Util.encodeByMD5("憔悴也相关"));
            user.setEmail("shakespeare@163.com");
            user = userRepository.save(user);
            log.info("saveUser, user : {}", user);
        }
    
    }

    运行测试方法,抛出异常:

    2018-03-28 19:25:49.669  INFO 8324 --- [           main] com.mj.wcs.service.UserServiceTest       : Started UserServiceTest in 15.072 seconds (JVM running for 16.826)
    Hibernate: 
        select
            next_val as id_val 
        from
            hibernate_sequence for update
    
    Hibernate: 
        update
            hibernate_sequence 
        set
            next_val= ? 
        where
            next_val=?
    Hibernate: 
        insert 
        into
            user
            (email, password, username, id) 
        values
            (?, ?, ?, ?)
    2018-03-28 19:25:50.003  WARN 8324 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1062, SQLState: 23000
    2018-03-28 19:25:50.003 ERROR 8324 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Duplicate entry '1' for key 'PRIMARY'
    2018-03-28 19:25:50.006  INFO 8324 --- [           main] o.h.e.j.b.internal.AbstractBatchImpl     : HHH000010: On release of batch it still contained JDBC statements
    
    org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
        ... ? more
    Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
        ... ? more
    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
        ... ? more

    定位异常原因可能是 User 实体类中使用 Lombok 提供的 @Data 注解导致 equals() 方法和 hashCode() 方法重写不正确,使用 IDEA 重新生成即可。