【Spring Cloud 基础设施搭建系列】Spring Cloud Demo项目 整合Spring Data JPA

mac2026-04-22  7

文章目录

Docker 安装Mysql整合Spring Data JPA引入依赖添加配置添加实体类JPA @MappedSuperclass 注解JPA @Column 注解JPA @Version 注解JPA @CreatedDate @CreatedBy @LastModifiedDate @LastModifiedBy 注解JPA @Id和@GeneratedValue 注解@JsonFormat注解 启动服务测试加入事务 源代码

Docker 安装Mysql

首先我们需要安装mysql,这里介绍一下使用docker去安装mysql

使用docker search 命令搜索mysql $ docker search mysql NAME DESCRIPTION STARS OFFICIAL AUTOMATED mysql MySQL is a widely used, open-source relation… 8719 [OK] mariadb MariaDB is a community-developed fork of MyS… 3049 [OK] mysql/mysql-server Optimized MySQL Server Docker images. Create… 645 [OK] centos/mysql-57-centos7 MySQL 5.7 SQL database server 63 centurylink/mysql Image containing mysql. Optimized to be link… 61 [OK] mysql/mysql-cluster Experimental MySQL Cluster Docker images. Cr… 54 deitch/mysql-backup REPLACED! Please use http://hub.docker.com/r… 41 [OK] bitnami/mysql Bitnami MySQL Docker Image 36 [OK] tutum/mysql Base docker image to run a MySQL database se… 34 schickling/mysql-backup-s3 Backup MySQL to S3 (supports periodic backup… 28 [OK] prom/mysqld-exporter 23 [OK] linuxserver/mysql A Mysql container, brought to you by LinuxSe… 22 centos/mysql-56-centos7 MySQL 5.6 SQL database server 16 circleci/mysql MySQL is a widely used, open-source relation… 15 mysql/mysql-router MySQL Router provides transparent routing be… 13 arey/mysql-client Run a MySQL client from a docker container 11 [OK] imega/mysql-client Size: 36 MB, alpine:3.5, Mysql client: 10.1.… 8 [OK] openshift/mysql-55-centos7 DEPRECATED: A Centos7 based MySQL v5.5 image… 6 yloeffler/mysql-backup This image runs mysqldump to backup data usi… 6 [OK] fradelg/mysql-cron-backup MySQL/MariaDB database backup using cron tas… 4 [OK] ansibleplaybookbundle/mysql-apb An APB which deploys RHSCL MySQL 2 [OK] genschsa/mysql-employees MySQL Employee Sample Database 2 [OK] jelastic/mysql An image of the MySQL database server mainta… 1 monasca/mysql-init A minimal decoupled init container for mysql 0 widdpim/mysql-client Dockerized MySQL Client (5.7) including Curl… 0 [OK] 使用docker pull命令拉取mysql镜像 $ docker pull mysql Using default tag: latest latest: Pulling from library/mysql 80369df48736: Pull complete e8f52315cb10: Pull complete cf2189b391fc: Pull complete cc98f645c682: Pull complete 27a27ac83f74: Pull complete fa1f04453414: Pull complete d45bf7d22d33: Pull complete 3dbac26e409c: Pull complete 9017140fb8c1: Pull complete b76dda2673ae: Pull complete bea9eb46d12a: Pull complete e1f050a38d0f: Pull complete Digest: sha256:7345ce4ce6f0c1771d01fa333b8edb2c606ca59d385f69575f8e3e2ec6695eee Status: Downloaded newer image for mysql:latest docker.io/library/mysql:latest 启动mysql docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql -p 3306:3306:将容器的3306端口映射到主机的3306端口 -v /opt/docker_v/mysql/conf:/etc/mysql/conf.d:将主机/opt/docker_v/mysql/conf目录挂载到容器的/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456:初始化root用户的密码 -d: 后台运行容器,并返回容器ID 进入容器 docker exec -it mysql bash 连接mysql #登录mysql mysql -u root -p

参考:Docker 安装 MySQL

整合Spring Data JPA

在开始之前,我先说明一下,这个项目只是拿来练手和学习的,并不是实际的项目,真实的项目结构和模块可能是很不一样的。

引入依赖

首先我这里抽取了一个common的module,用来存放跟jpa有关的common类和方法。命名为cloud-jpa-common。

首先我们需要在cloud-jpa-common中引入Spring Data JPA的依赖还有mysql连接的依赖。

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

然后我这里只用一个微服务cloud-service-member来做简单的改造。

我们需要在cloud-service-member中引入cloud-jpa-common

<dependency> <groupId>com.cc.cloud</groupId> <artifactId>cloud-jpa-common</artifactId> </dependency>

添加配置

下面我们需要添加连接mysql的配置还有jpa的配置。

spring: application: #对应config server所获取的配置文件的{application} name: cloud-service-member datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.99.100:3306/spring_cloud_demo?useUnicode=true&character_set_server=utf8mb4&useSSL=false&allowPublicKeyRetrieval=true username: root password: root jpa: #Hibernate相关配置 hibernate: # create每次运行都删除原有表创建新表,update不用每次创建新表 ddl-auto: create database: mysql # 打印SQL语句 show-sql: true properties: hibernate: format_sql: true jdbc: time_zone: UTC

关于sring.datasource.url的配置可以参考如下:

JDBC 连接 MySQL 时碰到的小坑

MySQL8版本数据库连接URL注意事项

关于JDBC连接数据库时出现的Public Key Retrieval is not allowed错误

永远不要在 MySQL 中使用「utf8」

mysql使用utf8mb4经验吐血总结

全面了解mysql中utf8和utf8mb4的区别

springboot连接JDBC及application.yml的注意事项

还有关于时区的设置,Hibernate支持设置时区,在Springboot中增加配置如下spring.jpa.properties.hibernate.jdbc.time_zone设置为UTC,当然还可以把spring.jpa.properties.hibernate.jdbc.time_zone设置为GMT+8。 如果是MySQL数据库,也可以在连接池链接后面增加配置如下:?serverTimezone=TimeZone&useLegacyDatetimeCode=false

参考:

UTC时间、GMT时间、本地时间、Unix时间戳

UTC和GMT时间

Java中的Date和时区转换

SpringBoot 统一时区的方案

spring boot 设置时区

添加实体类

package com.cc.cloud.member.domain; import com.cc.cloud.domain.common.VersionedEntityWithID; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import java.io.Serializable; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @NoArgsConstructor @Table(name = "MEMBER") @Entity public class Member extends VersionedEntityWithID implements Serializable { private static final long serialVersionUID = 1L; @Column(name = "MEMBER_NAME",length = 20,nullable = false) private String memberName; }

参考:

Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释

Entity实体类为什么要实现Serializable接口才能被序列化?

实体类为什么要实现Serializable序列化的作用

这里我使用了lombok,所以还需要引入依赖。

<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency>

当然lombok的使用也是存在一些坑的,可以参考如下:

lombok @EqualsAndHashCode 注解的影响

Lombok介绍和使用

Lombok 看这篇就够了

180. Spring Boot lombok:@EqualsAndHashCode

使用Hibernate、JPA、Lombok遇到的有趣问题

然后在我们的Entity中还继承了VersionedEntityWithID,VersionedEntityWithID是一个Entity中common的属性,我把他抽取到了cloud-jpa-common模块中去了。

package com.cc.cloud.domain.common; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @MappedSuperclass public class VersionedEntityWithID extends VersionedEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; }

VersionedEntity也是common的属性。

package com.cc.cloud.domain.common; import com.cc.cloud.constant.EntityConstant; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import javax.persistence.Version; import java.util.Date; @Data @MappedSuperclass @EntityListeners(AuditingEntityListener.class) class VersionedEntity { @Column(name = "JPA_VERSION") @Version private Long version; @Column(name = "CREATE_DATE") @JsonFormat( shape = JsonFormat.Shape.STRING, pattern = EntityConstant.JSON_DATE_FORMAT, timezone = EntityConstant.JSON_DATE_TIME_ZONE) @CreatedDate private Date createDate; @Column(name = "CREATE_BY") @CreatedBy private String createBy; @Column(name = "UPDATE_DATE") @JsonFormat( shape = JsonFormat.Shape.STRING, pattern = EntityConstant.JSON_DATE_FORMAT, timezone = EntityConstant.JSON_DATE_TIME_ZONE) @LastModifiedDate private Date updateDate; @Column(name = "UPDATE_BY") @LastModifiedBy private String updateBy; }

JPA @MappedSuperclass 注解

我们可以注意到上面的 @MappedSuperclass 注解

在Jpa里, 当我们在定义多个实体类时, 可能会遇到这几个实体类都有几个共同的属性, 这时就会出现很多重复代码. 这时我们可以选择编写一个父类,将这些共同属性放到这个父类中, 并且在父类上加上@MappedSuperclass注解.注意:标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。

标注为@MappedSuperclass的类不能再标注@Entity或@Table注解,也无需实现序列化接口.

参考:

springboot之jpa开发@MappedSuperclass 注解说明

@MappedSuperclass的作用

@MappedSuperclass的用法

SpringData JPA 中 @MappedSuperclass 注解的使用

JPA @Column 注解

@Column注解是用来标识实体类中属性与数据表中字段的对应关系。

参考:

JPA @Column 注解

Spring Date JPA的@Column注解详解

JPA @Version 注解

JPA默认提供了一个注解@Version来实现乐观锁。简单来说就是用一个version字段来充当乐观锁的作用。

参考:

【Spring】27、JPA 实现乐观锁@Version注解的使用

JPA之@Version进行乐观锁并发更新

JPA @CreatedDate @CreatedBy @LastModifiedDate @LastModifiedBy 注解

@CreatedDate 表示该字段为创建时间时间字段,在这个实体被insert的时候,会设置值@CreatedBy 表示该字段为创建人,在这个实体被insert的时候,会设置值@LastModifiedDate、@LastModifiedBy同理。

当然想要@CreatedDate @CreatedBy @LastModifiedDate @LastModifiedBy注解起作用,还需要做多几个步骤。

实体类上添加 @EntityListeners(AuditingEntityListener.class),我们在VersionedEntity中已经添加了@EntityListeners(AuditingEntityListener.class)在启动类上添加 @EnableJpaAuditing package com.cc.cloud.member; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableEurekaClient @EnableFeignClients @EnableCircuitBreaker @EnableJpaAuditing public class MemberApp { public static void main(String[] args) { SpringApplication.run(MemberApp.class, args); } }

这个时候@CreatedDate和@LastModifiedDate都已经起作用了。但是@CreatedBy,@LastModifiedBy还需要做额外的配置。

下面这两个配置类,我都是抽取到了cloud-jpa-common模块中了。

package com.cc.cloud.configuration; import org.springframework.data.domain.AuditorAware; import java.util.Optional; public class AuditorProvider implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { return Optional.of("system"); } } package com.cc.cloud.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; @Configuration public class AuditorConfig { @Bean public AuditorAware<String> auditorProvider() { return new AuditorProvider(); } }

参考:

Spring JPA 使用@CreatedDate、@CreatedBy、@LastModifiedDate、@LastModifiedBy 自动生成时间和修改者

Spring Data JPA 的时间注解:@CreatedDate 和 @LastModifiedDate

JPA EnableJpaAuditing 审计功能

JPA @CreatedBy@CreatedDate@LastModifiedBy@LastModifiedDate

Spring Data审计功能@CreatedDate、@CreatedBy、@LastModifiedDate、@LastModifiedBy的使用

JPA @Id和@GeneratedValue 注解

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

参考:

理解JPA注解@GeneratedValue

ID生成策略一 generator @GeneratedValue

@JsonFormat注解

@Column(name = "CREATE_DATE") @JsonFormat( shape = JsonFormat.Shape.STRING, pattern = EntityConstant.JSON_DATE_FORMAT, timezone = EntityConstant.JSON_DATE_TIME_ZONE) @CreatedDate private Date createDate; package com.cc.cloud.constant; public class EntityConstant { public static final String JSON_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String JSON_DATE_TIME_ZONE = "GMT+8"; }

参考:

yyyy-MM-dd’T’HH:mm:ss.SSS’Z’即UTC时间,与String日期转换

@JsonFormat与@DateTimeFormat注解的使用

使用@JsonFormat引起的时间比正常时间慢8小时解决方法

启动服务

现在我们可以启动我们的服务了,启动cloud-eureka,cloud-config-server,还有cloud-service-member。

启动cloud-service-member时候,发现在控制台打印如下:

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

所以我们需要使用driver-class-name: com.mysql.cj.jdbc.Driver

我们更改一下我们的配置:

spring: application: #对应config server所获取的配置文件的{application} name: cloud-service-member datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.99.100:3306/spring_cloud_demo?useUnicode=true&character_set_server=utf8mb4&useSSL=false&allowPublicKeyRetrieval=true username: root password: root jpa: #Hibernate相关配置 hibernate: # create每次运行都删除原有表创建新表,update不用每次创建新表 ddl-auto: create database: mysql # 打印SQL语句 show-sql: true properties: hibernate: format_sql: true jdbc: time_zone: UTC

然后我们重新启动服务,结果在控制台中又报了如下的错误。

Hibernate: drop table if exists member 2019-10-29 23:49:55.725 WARN [cloud-service-member,,,] 13364 --- [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL " drop table if exists member" via JDBC Statement org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL " drop table if exists member" via JDBC Statement at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.internal.SchemaDropperImpl.applySqlString(SchemaDropperImpl.java:375) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.internal.SchemaDropperImpl.applySqlStrings(SchemaDropperImpl.java:359) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropFromMetadata(SchemaDropperImpl.java:241) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.internal.SchemaDropperImpl.performDrop(SchemaDropperImpl.java:154) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:126) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:112) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:144) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) [spring-orm-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) [spring-orm-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) [spring-orm-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) [spring-orm-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) [spring-orm-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1804) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1741) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) [spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1083) ~[spring-context-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:853) ~[spring-context-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) ~[spring-context-5.1.3.RELEASE.jar:5.1.3.RELEASE] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE] at com.cc.cloud.member.MemberApp.main(MemberApp.java:17) ~[classes/:na] Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'member' at line 1 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120) ~[mysql-connector-java-8.0.13.jar:8.0.13] at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.13.jar:8.0.13] at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.13.jar:8.0.13] at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:782) ~[mysql-connector-java-8.0.13.jar:8.0.13] at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:666) ~[mysql-connector-java-8.0.13.jar:8.0.13] at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95) ~[HikariCP-3.2.0.jar:na] at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~[HikariCP-3.2.0.jar:na] at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:54) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] ... 34 common frames omitted Hibernate: create table member ( id integer not null, create_by varchar(255), create_date_gmt datetime, update_by varchar(255), update_date_gmt datetime, jpa_version bigint, member_name varchar(20) not null, primary key (id) ) engine=MyISAM 2019-10-29 23:49:55.732 WARN [cloud-service-member,,,] 13364 --- [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL " create table member ( id integer not null, create_by varchar(255), create_date_gmt datetime, update_by varchar(255), update_date_gmt datetime, jpa_version bigint, member_name varchar(20) not null, primary key (id) ) engine=MyISAM" via JDBC Statement org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL " create table member ( id integer not null, create_by varchar(255), create_date_gmt datetime, update_by varchar(255), update_date_gmt datetime, jpa_version bigint, member_name varchar(20) not null, primary key (id) ) engine=MyISAM" via JDBC Statement

结果排查了很久,发现可能是table的名字member有什么冲突吧。解决方式就是把table名字改为members

package com.cc.cloud.member.domain; import com.cc.cloud.domain.common.VersionedEntityWithID; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import java.io.Serializable; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @NoArgsConstructor @Table(name = "MEMBERS") @Entity public class Member extends VersionedEntityWithID implements Serializable { private static final long serialVersionUID = 1L; @Column(name = "MEMBER_NAME",length = 20,nullable = false) private String memberName; }

参考:

springboot Error executing DDL via JDBC Statement

一个hql 关键字member(非mysql)引起的 vo 数据 保存数据库错误

Springboot+jpa自动生成表,以及可能会遇到的问题

测试

接下来我们在代码中写个测试的方法。

package com.cc.cloud.member.controller; import com.cc.cloud.member.domain.Member; import com.cc.cloud.member.feign.OrderFeign; import com.cc.cloud.member.service.MemberService; import com.google.common.collect.Lists; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import java.util.List; @RefreshScope @RestController @RequestMapping("/member") public class MemberController { private OrderFeign orderFeign; @Value("${cloud.service.member}") private String memberConfig; @Autowired private MemberService memberService; @Autowired public void setOrderFeign(OrderFeign orderFeign) { this.orderFeign = orderFeign; } @RequestMapping("/orders") @ResponseStatus(HttpStatus.OK) public List<String> getOrderList() { return orderFeign.getAllOrderList(); } @RequestMapping("/members") @ResponseStatus(HttpStatus.OK) public List<String> getMemberList() { List<String> memberList = Lists.newArrayList(); memberList.add("member 1"); memberList.add("member 2"); memberList.add("member 3"); return memberList; } @GetMapping("/config") @ResponseStatus(HttpStatus.OK) public String getMemberConfig(){ return memberConfig; } //加入测试的代码 @PostMapping @ResponseStatus(HttpStatus.OK) public String addMember(@RequestBody Member member){ return memberService.addMember(member).toString(); } }

接下来的是service层的代码。

package com.cc.cloud.member.service.impl; import com.cc.cloud.member.domain.Member; import com.cc.cloud.member.repository.MemberRepository; import com.cc.cloud.member.service.MemberService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class MemberServiceImpl implements MemberService { @Autowired private MemberRepository memberRepository; @Override public Member addMember(Member member) { return memberRepository.save(member); } }

repository层的代码。

package com.cc.cloud.member.repository; import com.cc.cloud.member.domain.Member; import org.springframework.data.jpa.repository.JpaRepository; public interface MemberRepository extends JpaRepository<Member, Integer> { }

启动完毕之后,我们测试我们的API接口。

结果发现@CreateBy是null,这是为什么。

经过排查了很久,原因是因为启动类中@SpringBootApplication注解是扫描当前包和子包,而当前包是com.cc.cloud.member,而AuditorConfig类,所在的包在com.cc.cloud.configuration,所以根本扫不到配置。解决方法也很简单,把包扫描设置为com.cc.cloud

package com.cc.cloud.member; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication(scanBasePackages = "com.cc.cloud") @EnableEurekaClient @EnableFeignClients @EnableCircuitBreaker @EnableJpaAuditing public class MemberApp { public static void main(String[] args) { SpringApplication.run(MemberApp.class, args); } }

然后重新启动,重新测试我们的API接口。

现在发现已经能看到createBy的值。

加入事务

我们只需要在我们的service层中加入注解@Transactional

@Transactional @Override public Member addMember(Member member) { return memberRepository.save(member); }

然后在启动类中开启注解事务管理。

package com.cc.cloud.member; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication(scanBasePackages = "com.cc.cloud") @EnableEurekaClient @EnableFeignClients @EnableCircuitBreaker @EnableJpaAuditing @EnableTransactionManagement // 开启注解事务管理 public class MemberApp { public static void main(String[] args) { SpringApplication.run(MemberApp.class, args); } }

但是经过测试,当我在service中加入错误的代码,结果发现最终并没有回滚,依然插入了数据。

@Transactional @Override public Member addMember(Member member) { Member m = memberRepository.save(member); int i = 1 / 0; return m; }

这是为什么,原因是因为mysql中engine默认是myisam,它是事务不安全的。解决方法就是修改我们的配置。

加入配置database-platform: org.hibernate.dialect.MySQL5InnoDBDialect,设置为engine=innodb

配置如下:

spring: application: #对应config server所获取的配置文件的{application} name: cloud-service-member datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.99.100:3306/spring_cloud_demo?useUnicode=true&character_set_server=utf8mb4&useSSL=false&allowPublicKeyRetrieval=true username: root password: root jpa: #Hibernate相关配置 hibernate: # create每次运行都删除原有表创建新表,update不用每次创建新表 ddl-auto: create database: mysql # 打印SQL语句 show-sql: true database-platform: org.hibernate.dialect.MySQL5InnoDBDialect #设置使用的engine=InnoDB,默认MyISAM 不是事务安全的 properties: hibernate: format_sql: true jdbc: time_zone: UTC

参考:

Spring Boot 事务的使用

SpringBoot 实战 (十) | 声明式事务

Spring Boot中的事务管理

mysql中engine=innodb和engine=myisam的区别

MySql – 创建表时 engine=innodb和engine=myisam的区别

源代码

https://gitee.com/cckevincyh/spring-cloud-demo/tree/spring-data-jpa/

c. 认证博客专家 MySQL Java Elasticsearch github:https://github.com/cckevincyhgitee:https://gitee.com/cckevincyh
最新回复(0)