Spring Jpa這個(gè)問(wèn)題怎么解決?
在使用spring-boot-starter-data-jpa時(shí),通過(guò)這樣的配置可以在程序啟動(dòng)后實(shí)現(xiàn)在指定數(shù)據(jù)庫(kù)自動(dòng)建表。
spring:
jpa:
hibernate:
ddl-auto: update
但是這種方式建表后沒(méi)辦法為每一列增加對(duì)應(yīng)的中文注釋,有什么辦法可以實(shí)現(xiàn)這一需求呢?
后面在網(wǎng)上找到了實(shí)現(xiàn)方法:
<dependency>
<groupId>com.github.biyanwen</groupId>
<artifactId>jpa-comment-spring-boot-starter</artifactId>
<version>1.0.2</version>
</dependency>
但是在當(dāng)前項(xiàng)目中無(wú)效,后面發(fā)現(xiàn)部分依賴已經(jīng)改變,應(yīng)該是對(duì)高版本JPA不支持導(dǎo)致。今天基于該jar重新梳理實(shí)現(xiàn)過(guò)程。
實(shí)現(xiàn)方式
基于自定義注解以及Spring自動(dòng)配置實(shí)現(xiàn)。
- 定義注解Comment,該注解定義在字段上,定義該列的中文描述。
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Comment {
/**
* 注釋的值
*
* @return {@link String}
*/
String value() default "";
}
- spring jpa是基于Habernate實(shí)現(xiàn),同樣我們需要基于接口org.hibernate.integrator.spi.Integrator,在生成ddl時(shí)進(jìn)行擴(kuò)展。
public class CommentIntegrator implements Integrator {
public static final CommentIntegrator INSTANCE = new CommentIntegrator();
public CommentIntegrator() {
super();
}
@Override
public void integrate(Metadata metadata, BootstrapContext bootstrapContext, SessionFactoryImplementor sessionFactory) {
processComment(metadata);
}
/**
* Not used.
*
* @param sessionFactoryImplementor The session factory being closed.
* @param sessionFactoryServiceRegistry That session factory's service registry
*/
@Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
}
/**
* 生成注釋代碼
*
* @param metadata process annotation of this {@code Metadata}.
*/
protected void processComment(Metadata metadata) {
for (PersistentClass persistentClass : metadata.getEntityBindings()) {
Class<?> clz = persistentClass.getMappedClass();
if (clz.isAnnotationPresent(Comment.class)) {
Comment comment = clz.getAnnotation(Comment.class);
persistentClass.getTable().setComment(comment.value());
}
Property identifierProperty = persistentClass.getIdentifierProperty();
if (identifierProperty != null) {
propertyComment(persistentClass, identifierProperty.getName());
} else {
org.hibernate.mapping.Component component = persistentClass.getIdentifierMapper();
if (component != null) {
Iterator<Property> iterator = component.getPropertyIterator();
while (iterator.hasNext()) {
propertyComment(persistentClass, iterator.next().getName());
}
}
}
Iterator<Property> iterator = persistentClass.getProperties().iterator();
while (iterator.hasNext()) {
propertyComment(persistentClass, iterator.next().getName());
}
}
}
/**
* 為屬性生成注釋
*
* @param persistentClass Hibernate {@code PersistentClass}
* @param columnName name of field
*/
private void propertyComment(PersistentClass persistentClass, String columnName) {
try {
String comment = getPropertyComment(persistentClass, columnName);
Value value = persistentClass.getProperty(columnName).getValue();
if( value.getColumns().iterator().hasNext() ){
String sqlColumnName = value.getColumns().iterator().next().getText();
Iterator<org.hibernate.mapping.Column> columnIterator = persistentClass.getTable().getColumns().iterator();
while (columnIterator.hasNext()) {
org.hibernate.mapping.Column column = columnIterator.next();
if (sqlColumnName.equalsIgnoreCase(column.getName())) {
column.setComment(comment);
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String getPropertyComment(PersistentClass persistentClass, String columnName) throws Exception {
String comment = null;
Field field = ReflectionUtils.findField(persistentClass.getMappedClass(), columnName);
if (field != null) {
if (field.isAnnotationPresent(Comment.class)) {
comment = field.getAnnotation(Comment.class).value();
} else {
PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), persistentClass.getMappedClass());
Method readMethod = descriptor.getReadMethod();
Comment comment1 = readMethod.getAnnotation(Comment.class);
if (comment1 != null) {
comment = comment1.value();
}
}
}
return comment;
}
}
- 定義配置類,對(duì)HibernatePropertiesCustomizer進(jìn)行擴(kuò)展。
public class HibernateProperties implements HibernatePropertiesCustomizer {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("hibernate.integrator_provider", (IntegratorProvider) () -> Collections.singletonList(CommentIntegrator.INSTANCE));
}
}
- 定義spring配置,實(shí)現(xiàn)自動(dòng)裝配。
在resource目錄添加自動(dòng)注入配置META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,這樣通過(guò)引入jar就可以自動(dòng)使用該配置。
cn.cycad.jpa.comment.config.CommentConfig
應(yīng)用示例
- 比如現(xiàn)在有一個(gè)User實(shí)體,我們通過(guò)繼承基類。
@Entity
@Table(name = "t_user")
@Data
public class User extends Domain {
@Id
@Comment("業(yè)務(wù)主鍵")
private String id;
@Comment("用戶名稱")
private String caption;
@Comment("用戶年齡")
private Integer age;
}
- 啟動(dòng)服務(wù)后,可以看到控制臺(tái)輸出的建表語(yǔ)句信息。
Hibernate:
create table t_user (
id varchar(255) not null,
create_time timestamp(6),
creator varchar(56),
modified_time timestamp(6),
modifier varchar(56),
age integer,
caption varchar(255),
primary key (id)
)
Hibernate:
comment on column t_user.id is
'業(yè)務(wù)主鍵'
Hibernate:
comment on column t_user.age is
'用戶年齡'
Hibernate:
comment on column t_user.caption is
'用戶名稱'