├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── example │ └── demo │ ├── DemoApplication.java │ ├── DemoTable.java │ ├── comment │ ├── Comment.java │ └── CommentIntegrator.java │ └── configuration │ └── HibernateConfig.java └── resources └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Elyar Adil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JpaCommentAnnotation 2 | 3 | Comment annotation support for JPA DDL generation. 4 | 5 | To setup the integrator, add this config class: 6 | ```java 7 | @Component 8 | public class HibernateConfig implements HibernatePropertiesCustomizer { 9 | @Override 10 | public void customize(Map hibernateProperties) { 11 | hibernateProperties.put("hibernate.use_sql_comments", true); 12 | hibernateProperties.put("hibernate.integrator_provider", 13 | (IntegratorProvider) () -> Collections.singletonList(CommentIntegrator.INSTANCE)); 14 | } 15 | } 16 | ``` 17 | Code sample: 18 | ```java 19 | @Data 20 | @Entity 21 | @Comment("Demo table comment") 22 | @Table(name = "demo_table") 23 | public class DemoTable { 24 | 25 | @Id 26 | @GeneratedValue 27 | @Comment("ID comment") 28 | @Column(name = "id") 29 | private Integer id; 30 | 31 | @Comment("Value comment") 32 | @Column(name = "value") 33 | private String value; 34 | 35 | } 36 | ``` 37 | 38 | ## License 39 | This project is licensed under the terms of the **MIT** license. 40 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.0.RELEASE 9 | 10 | 11 | com.example 12 | demo 13 | 0.0.1-SNAPSHOT 14 | demo 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | 28 | mysql 29 | mysql-connector-java 30 | runtime 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-maven-plugin 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | import org.springframework.boot.SpringApplication; 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | @SpringBootApplication 6 | public class DemoApplication { 7 | 8 | public static void main(String[] args) { 9 | SpringApplication.run(DemoApplication.class, args); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/DemoTable.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import com.example.demo.comment.Comment; 4 | import lombok.Data; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | 12 | @Data 13 | @Table 14 | @Entity 15 | @Comment("Table comment") 16 | public class DemoTable { 17 | @Id 18 | @GeneratedValue 19 | @Column 20 | @Comment("Identifier comment") 21 | private Integer id; 22 | 23 | @Column 24 | @Comment("Field comment") 25 | private String field; 26 | 27 | } -------------------------------------------------------------------------------- /src/main/java/com/example/demo/comment/Comment.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.comment; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) 10 | public @interface Comment { 11 | String value() default ""; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/comment/CommentIntegrator.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.comment; 2 | 3 | import org.hibernate.boot.Metadata; 4 | import org.hibernate.engine.spi.SessionFactoryImplementor; 5 | import org.hibernate.integrator.spi.Integrator; 6 | import org.hibernate.mapping.PersistentClass; 7 | import org.hibernate.mapping.Property; 8 | import org.hibernate.service.spi.SessionFactoryServiceRegistry; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.persistence.Column; 12 | import java.lang.reflect.Field; 13 | import java.util.Iterator; 14 | 15 | /** 16 | * Integrator used to process comment annotation. 17 | * 18 | * @author Elyar Adil 19 | * @since 1.0 20 | */ 21 | @Component 22 | public class CommentIntegrator implements Integrator { 23 | public static final CommentIntegrator INSTANCE = new CommentIntegrator(); 24 | 25 | public CommentIntegrator() { 26 | super(); 27 | } 28 | 29 | /** 30 | * Perform comment integration. 31 | * 32 | * @param metadata The "compiled" representation of the mapping information 33 | * @param sessionFactory The session factory being created 34 | * @param serviceRegistry The session factory's service registry 35 | */ 36 | @Override 37 | public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { 38 | processComment(metadata); 39 | } 40 | 41 | /** 42 | * Not used. 43 | * 44 | * @param sessionFactoryImplementor The session factory being closed. 45 | * @param sessionFactoryServiceRegistry That session factory's service registry 46 | */ 47 | @Override 48 | public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) { 49 | } 50 | 51 | /** 52 | * Process comment annotation. 53 | * 54 | * @param metadata process annotation of this {@code Metadata}. 55 | */ 56 | private void processComment(Metadata metadata) { 57 | for (PersistentClass persistentClass : metadata.getEntityBindings()) { 58 | // Process the Comment annotation is applied to Class 59 | Class clz = persistentClass.getMappedClass(); 60 | if (clz.isAnnotationPresent(Comment.class)) { 61 | Comment comment = clz.getAnnotation(Comment.class); 62 | persistentClass.getTable().setComment(comment.value()); 63 | } 64 | 65 | // Process Comment annotations of identifier. 66 | Property identifierProperty = persistentClass.getIdentifierProperty(); 67 | if (identifierProperty != null) { 68 | fieldComment(persistentClass, identifierProperty.getName()); 69 | } else { 70 | org.hibernate.mapping.Component component = persistentClass.getIdentifierMapper(); 71 | if (component != null) { 72 | //noinspection unchecked 73 | Iterator iterator = component.getPropertyIterator(); 74 | while (iterator.hasNext()) { 75 | fieldComment(persistentClass, iterator.next().getName()); 76 | } 77 | } 78 | } 79 | // Process fields with Comment annotation. 80 | //noinspection unchecked 81 | Iterator iterator = persistentClass.getPropertyIterator(); 82 | while (iterator.hasNext()) { 83 | fieldComment(persistentClass, iterator.next().getName()); 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Process @{code comment} annotation of field. 90 | * 91 | * @param persistentClass Hibernate {@code PersistentClass} 92 | * @param columnName name of field 93 | */ 94 | private void fieldComment(PersistentClass persistentClass, String columnName) { 95 | try { 96 | Field field = persistentClass.getMappedClass().getDeclaredField(columnName); 97 | if (field.isAnnotationPresent(Comment.class)) { 98 | String comment = field.getAnnotation(Comment.class).value(); 99 | String sqlColumnName= persistentClass.getProperty(columnName).getValue().getColumnIterator().next().getText(); 100 | Iterator columnIterator = persistentClass.getTable().getColumnIterator(); 101 | while (columnIterator.hasNext()) { 102 | org.hibernate.mapping.Column column = columnIterator.next(); 103 | if (sqlColumnName.equalsIgnoreCase(column.getName())) { 104 | column.setComment(comment); 105 | break; 106 | } 107 | } 108 | } 109 | } catch (NoSuchFieldException | SecurityException ignored) { 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/configuration/HibernateConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.configuration; 2 | 3 | import com.example.demo.comment.CommentIntegrator; 4 | import org.hibernate.jpa.boot.spi.IntegratorProvider; 5 | import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Collections; 9 | import java.util.Map; 10 | 11 | @Component 12 | public class HibernateConfig implements HibernatePropertiesCustomizer { 13 | @Override 14 | public void customize(Map hibernateProperties) { 15 | hibernateProperties.put("hibernate.use_sql_comments", true); 16 | hibernateProperties.put("hibernate.integrator_provider", 17 | (IntegratorProvider) () -> Collections.singletonList(CommentIntegrator.INSTANCE)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 2 | spring.datasource.url=jdbc:mysql://localhost:3306/database?serverTimezone=GMT%2B8 3 | spring.datasource.username=root 4 | spring.datasource.password=password 5 | 6 | spring.jpa.hibernate.ddl-auto=update 7 | --------------------------------------------------------------------------------