最近有个项目因为命名策略的原因导致数据库表的不符合标准,所以看了下源码,废话不多说上代码!
现有的命名策略
- jpa现有的命名策略设置:hibernate.implicit_naming_strategy(隐式),hibernate.physical_naming_strategy(物理显示)
- implicit_naming_strategy 有5个策略:实现ImplicitNamingStrategy接口,其中SpringImplicitNamingStrategy策略是实现了ImplicitNamingStrategyJpaCompliantImpl只是重写了关联表的表名方法
- physical_naming_strategy 有2个策略:实现PhysicalNamingStrategy接口,
分别是PhysicalNamingStrategyStandardImpl、SpringPhysicalNamingStrategy。其中SpringPhysicalNamingStrategy 会将所有的大写都转成小写并加"_".
项目大概
使用SpringBoot2.4.2,mysql5.7
项目目录: 我这里因为项目需要用的是多数据源,隐式策略为默认,数据源1物理策略默认,数据源2物理策略为 SpringPhysicalNamingStrategy
数据源读取配置文件并注入
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource(){return DataSourceBuilder.create().build();
}
@Bean(name = "secondDataSource")
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource secondDataSource(){return DataSourceBuilder.create().build();
}
}
**主数据源**
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory", // 配置连接工厂 entityManagerFactory
transactionManagerRef = "primaryTransactionManager", // 配置 事物管理器 transactionManager
basePackages = {"com.mj.jpaname.primary.repository"})
public class PrimaryDataSourceConfig {
@Autowired
private Environment env;
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Primary
@Bean(name = "primaryEntityManager")
public EntityManager entityManager() {
return primaryEntityManagerFactory().getObject().createEntityManager();
}
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager() {
EntityManagerFactory factory = primaryEntityManagerFactory().getObject();
return new JpaTransactionManager(factory);
}
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(primaryDataSource);
factory.setPackagesToScan("com.mj.jpaname.primary.domain");
factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.hbm2ddl.auto", "update");
jpaProperties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
jpaProperties.put("hibernate.format_sql", env.getProperty("spring.jpa.properties.hibernate.format_sql"));
jpaProperties.put("hibernate.dialect", env.getProperty("spring.jpa.hibernate.dialect"));
jpaProperties.put("hibernate.current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext");
factory.setJpaProperties(jpaProperties);
factory.setPersistenceUnitName("primaryPersistenceUnit");
return factory;
}
}
二数据源:将primary改成second ,并在secondEntityManagerFactory()方法中加入
jpaProperties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
配置文件
spring:
datasource:
primary:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/cs?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
second:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/cs2?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
jpa:
# 显示sql
show-sql: false
properties:
hibernate:
format_sql: true
hibernate:
ddl-auto: update
dialect: org.hibernate.dialect.MySQL55Dialect
实体类
@Entity
@Setter
@Getter
@NoArgsConstructor
@Table(name = "t_teacher")
public class TeacherEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "create_date")
private Date createDate;
@Column(name = "update_d")
private Date updateDate;
}
@Entity
@Setter
@Getter
@NoArgsConstructor
public class StudentEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Date createDate;
private Date updateDate;
}
@Entity
@Setter
@Getter
@NoArgsConstructor
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Date createDate;
}
@Entity
@Setter
@Getter
@NoArgsConstructor
@Table(name = "t_things")
public class ThingsEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@Column(name = "create_date")
private Date createDate;
}
分析
- 首先在我们设置的2个数据源配置的工厂类 LocalContainerEntityManagerFactoryBean 继承了AbstractEntityManagerFactoryBean 抽象类,该抽象类实现了InitializingBean,所以必须实现afterPropertiesSet()方法,在初始化bean的时候执行。所以在项目启动后会调用afterPropertiesSet(),里面会设置对应的命名策略。
- LocalContainerEntityManagerFactoryBean的afterPropertiesSet()会调用分类的afterPropertiesSet
- EntityManagerFactory emf = provider.createContainerEntityManagerFactory(this.persistenceUnitInfo, getJpaPropertyMap()); ,其中**getJpaPropertyMap()**就是我们在里面设置的配置 .
- 在EntityManagerFactoryBuilderImpl的构造方法中会建立对应的策略选择器
- 在建立策略选择器会将对应默认策略设置好已被后续的使用,隐式的命名策略也是在这里设置的
- 在MetadataBuilderImpl类中创建对象时, this.options = new MetadataBuildingOptionsImpl( serviceRegistry );
这步很关键,因为创建MetadataBuilderImpl时设置了命名策略且后面使用的命名策略都是从这个options得到的。
StrategySelectorImpl.resolveDefaultableStrategy() 该类根据配置中有无设置对应的命名策略,其中隐身命名策略若没有设置,则都上面的策略选择器中获取default的ImplicitNamingStrategyJpaCompliantImpl策略,而显示命名策略默认获取PhysicalNamingStrategyStandardImpl 策略,这样命名策略设置完成。 - 开始对实体类管理器bulid建立,来建立对应的表、字段。
- 在MetadataBuildingProcess.complete会将之前的策略等设置包装成一个MetadataBuildingContextRootImpl 共后面使用.
- 在建立表的过程中会解析实体类上是否标注了**@Table** 这个注解
- 根据**@Table** 设置的属性来建立对应的表,根据是否有tableName来决定和命名策略来获得模型。
- 根据第8上面设置的MetadataBuildingContextRootImpl的options。若没有tableName则根据设置的隐式命名策略,获取对应模型名。若有tableName的话则用tableName。
- 最后会根据当前设置的物理策略来确定最后的表名
13.同期字段的话会根据是否添加**@Column** 注解来进行设置。
结果
需注意表名的大写会变成小写