Spring Jpa這個問題怎么解決?
作者:了不起
spring jpa是基于Habernate實現,同樣我們需要基于接口org.hibernate.integrator.spi.Integrator,在生成ddl時進行擴展。
在使用spring-boot-starter-data-jpa時,通過這樣的配置可以在程序啟動后實現在指定數據庫自動建表。
spring:
jpa:
hibernate:
ddl-auto: update
但是這種方式建表后沒辦法為每一列增加對應的中文注釋,有什么辦法可以實現這一需求呢?
后面在網上找到了實現方法:
<dependency>
<groupId>com.github.biyanwen</groupId>
<artifactId>jpa-comment-spring-boot-starter</artifactId>
<version>1.0.2</version>
</dependency>
但是在當前項目中無效,后面發現部分依賴已經改變,應該是對高版本JPA不支持導致。今天基于該jar重新梳理實現過程。
實現方式
基于自定義注解以及Spring自動配置實現。
- 定義注解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實現,同樣我們需要基于接口org.hibernate.integrator.spi.Integrator,在生成ddl時進行擴展。
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;
}
}
- 定義配置類,對HibernatePropertiesCustomizer進行擴展。
public class HibernateProperties implements HibernatePropertiesCustomizer {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("hibernate.integrator_provider", (IntegratorProvider) () -> Collections.singletonList(CommentIntegrator.INSTANCE));
}
}
- 定義spring配置,實現自動裝配。
在resource目錄添加自動注入配置META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,這樣通過引入jar就可以自動使用該配置。
cn.cycad.jpa.comment.config.CommentConfig
應用示例
- 比如現在有一個User實體,我們通過繼承基類。
@Entity
@Table(name = "t_user")
@Data
public class User extends Domain {
@Id
@Comment("業務主鍵")
private String id;
@Comment("用戶名稱")
private String caption;
@Comment("用戶年齡")
private Integer age;
}
- 啟動服務后,可以看到控制臺輸出的建表語句信息。
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
'業務主鍵'
Hibernate:
comment on column t_user.age is
'用戶年齡'
Hibernate:
comment on column t_user.caption is
'用戶名稱'
責任編輯:武曉燕
來源:
Java技術指北