首先我们需要安装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
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 -u root -p
参考:Docker 安装 MySQL
在开始之前,我先说明一下,这个项目只是拿来练手和学习的,并不是实际的项目,真实的项目结构和模块可能是很不一样的。
首先我这里抽取了一个common的module,用来存放跟jpa有关的common类和方法。命名为cloud-jpa-common。
首先我们需要在cloud-jpa-common中引入Spring Data JPA的依赖还有mysql连接的依赖。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
然后我这里只用一个微服务cloud-service-member来做简单的改造。
我们需要在cloud-service-member中引入cloud-jpa-common
<dependency>
<groupId>com.cc.cloudgroupId>
<artifactId>cloud-jpa-commonartifactId>
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.projectlombokgroupId>
<artifactId>lombokartifactId>
<scope>providedscope>
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;
}
我们可以注意到上面的 @MappedSuperclass
注解
在Jpa里, 当我们在定义多个实体类时, 可能会遇到这几个实体类都有几个共同的属性, 这时就会出现很多重复代码.
这时我们可以选择编写一个父类,将这些共同属性放到这个父类中, 并且在父类上加上@MappedSuperclass
注解.注意:标注为@MappedSuperclass
的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。
标注为@MappedSuperclass
的类不能再标注@Entity或@Table注解,也无需实现序列化接口.
参考:
springboot之jpa开发@MappedSuperclass 注解说明
@MappedSuperclass的作用
@MappedSuperclass的用法
SpringData JPA 中 @MappedSuperclass 注解的使用
@Column注解是用来标识实体类中属性与数据表中字段的对应关系。
参考:
JPA @Column 注解
Spring Date JPA的@Column注解详解
JPA默认提供了一个注解@Version来实现乐观锁。简单来说就是用一个version字段来充当乐观锁的作用。
参考:
【Spring】27、JPA 实现乐观锁@Version注解的使用
JPA之@Version进行乐观锁并发更新
当然想要@CreatedDate @CreatedBy @LastModifiedDate @LastModifiedBy注解起作用,还需要做多几个步骤。
@EntityListeners(AuditingEntityListener.class)
,我们在VersionedEntity中已经添加了@EntityListeners(AuditingEntityListener.class)
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的使用
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
参考:
理解JPA注解@GeneratedValue
ID生成策略一 generator @GeneratedValue
@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/