From c65cebebc100f0e1a0c0c56c5d1efc10017f9c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=8A=E6=85=B6=E5=A0=82?= Date: Wed, 3 Oct 2018 14:54:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20annotation=20=E8=99=95?= =?UTF-8?q?=E7=90=86=E5=A4=9A=E5=9C=8B=E8=AA=9E=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 +- .../controller/I18NTestController.java | 17 ++ .../springtest/controller/TestController.java | 2 +- .../springtest/entity/CrmSystemL1basic.java | 145 ++++++++++++++++++ .../springtest/hibernate/I18NInterceptor.java | 40 +++++ .../hibernate/I18NReadEventListener.java | 127 +++++++++++++++ .../springtest/hibernate/I18NTranslate.java | 34 ++++ .../SqlServerDialectWithNvarchar.java | 10 ++ .../springtest/repo/CrmSystemL1basicRepo.java | 6 + .../org.hibernate.integrator.spi.Integrator | 1 + src/main/resources/application.yml | 2 +- 11 files changed, 389 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java create mode 100644 src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java create mode 100644 src/main/java/org/ylhealth/ym/springtest/hibernate/I18NInterceptor.java create mode 100644 src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java create mode 100644 src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslate.java create mode 100644 src/main/java/org/ylhealth/ym/springtest/hibernate/SqlServerDialectWithNvarchar.java create mode 100644 src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java create mode 100644 src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator diff --git a/pom.xml b/pom.xml index 3bd577a..42607f9 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,12 @@ org.springframework spring-aspects + + + com.google.guava + guava + 23.0 + @@ -75,7 +81,7 @@ org.codehaus.mojo aspectj-maven-plugin - 1.6 + 1.11 diff --git a/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java b/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java new file mode 100644 index 0000000..a0b42ee --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java @@ -0,0 +1,17 @@ +package org.ylhealth.ym.springtest.controller; + +import javax.inject.Inject; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.ylhealth.ym.springtest.entity.CrmSystemL1basic; +import org.ylhealth.ym.springtest.repo.CrmSystemL1basicRepo; + +@RestController +public class I18NTestController { + @Inject CrmSystemL1basicRepo dao; + + @GetMapping("/i18n/Test1") + public CrmSystemL1basic test1() { + return dao.findOne("01"); + } +} diff --git a/src/main/java/org/ylhealth/ym/springtest/controller/TestController.java b/src/main/java/org/ylhealth/ym/springtest/controller/TestController.java index cd4eb3d..30f6632 100644 --- a/src/main/java/org/ylhealth/ym/springtest/controller/TestController.java +++ b/src/main/java/org/ylhealth/ym/springtest/controller/TestController.java @@ -15,7 +15,7 @@ public class TestController { @GetMapping("/Test1") public UserInfo test1() { - return userInfoService.findUserInfo("aaa1"); + return userInfoService.findUserInfo("aaa"); } @GetMapping("/create") diff --git a/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java b/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java new file mode 100644 index 0000000..6ef2bc5 --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java @@ -0,0 +1,145 @@ +package org.ylhealth.ym.springtest.entity; +// Generated Jul 20, 2016 5:22:20 PM by Hibernate Tools 3.2.2.GA + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Nationalized; +import org.ylhealth.ym.springtest.hibernate.I18NTranslate; +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** CrmSystemL1basic generated by hbm2java */ +@Entity +@Table(name = "CRM_SystemL1Basic") +public class CrmSystemL1basic implements java.io.Serializable { + + private String groupCode; + private String func; + + @I18NTranslate(column="groupName", id="groupCode", table = "CRM_SystemL1Basic_translation") + private String groupName; + private Character codeStatus; + private String status; + @JsonIgnore private String modifiedId; + @JsonIgnore private Date modifiedTime; + private Integer listSeq; + + public CrmSystemL1basic() {} + + public CrmSystemL1basic( + String groupCode, + String func, + String groupName, + String status, + String modifiedId, + Date modifiedTime) { + this.groupCode = groupCode; + this.func = func; + this.groupName = groupName; + this.status = status; + this.modifiedId = modifiedId; + this.modifiedTime = modifiedTime; + } + + public CrmSystemL1basic( + String groupCode, + String func, + String groupName, + Character codeStatus, + String status, + String modifiedId, + Date modifiedTime, + Integer listSeq) { + this.groupCode = groupCode; + this.func = func; + this.groupName = groupName; + this.codeStatus = codeStatus; + this.status = status; + this.modifiedId = modifiedId; + this.modifiedTime = modifiedTime; + this.listSeq = listSeq; + } + + @Id + @GenericGenerator(name = "increment", strategy = "uuid2") + @GeneratedValue(generator = "increment") + @Column(name = "GroupCode", unique = true, nullable = false, length = 20) + public String getGroupCode() { + return this.groupCode; + } + + public void setGroupCode(String groupCode) { + this.groupCode = groupCode; + } + + @Column(name = "Func", nullable = false, length = 20) + public String getFunc() { + return this.func; + } + + public void setFunc(String func) { + this.func = func; + } + + @Nationalized + @Column(name = "GroupName", nullable = false, length = 50) + public String getGroupName() { + return this.groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + @Column(name = "CodeStatus", length = 1) + public Character getCodeStatus() { + return this.codeStatus; + } + + public void setCodeStatus(Character codeStatus) { + this.codeStatus = codeStatus; + } + + @Column(name = "Status", nullable = false, length = 1) + public String getStatus() { + return this.status; + } + + public void setStatus(String status) { + this.status = status; + } + + @Column(name = "ModifiedId", nullable = false, length = 20) + public String getModifiedId() { + return this.modifiedId; + } + + public void setModifiedId(String modifiedId) { + this.modifiedId = modifiedId; + } + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "ModifiedTime", nullable = false, length = 23) + public Date getModifiedTime() { + return this.modifiedTime; + } + + public void setModifiedTime(Date modifiedTime) { + this.modifiedTime = modifiedTime; + } + + @Column(name = "ListSeq") + public Integer getListSeq() { + return this.listSeq; + } + + public void setListSeq(Integer listSeq) { + this.listSeq = listSeq; + } +} diff --git a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NInterceptor.java b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NInterceptor.java new file mode 100644 index 0000000..21fefdc --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NInterceptor.java @@ -0,0 +1,40 @@ +package org.ylhealth.ym.springtest.hibernate; + +import org.hibernate.boot.Metadata; +import org.hibernate.cfg.beanvalidation.DuplicationStrategyImpl; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.EventType; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 運用 org.hibernate.integrator.spi.Integrator 處理多國語系基本檔 + * 參考: + * https://github.com/deathman92/localized + */ +public class I18NInterceptor implements Integrator { + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public void integrate( + Metadata metadata, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + logger.debug("integrate..."); + + final EventListenerRegistry eventListenerRegistry = + serviceRegistry.getService(EventListenerRegistry.class); + eventListenerRegistry.addDuplicationStrategy(DuplicationStrategyImpl.INSTANCE); + eventListenerRegistry.appendListeners( + EventType.POST_LOAD, new I18NReadEventListener(sessionFactory)); + } + + @Override + public void disintegrate( + SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + logger.debug("disintegrate..."); + } +} diff --git a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java new file mode 100644 index 0000000..dcde458 --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java @@ -0,0 +1,127 @@ +package org.ylhealth.ym.springtest.hibernate; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.persistence.Id; +import org.hibernate.SQLQuery; +import org.hibernate.StatelessSession; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.spi.PostLoadEvent; +import org.hibernate.event.spi.PostLoadEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.i18n.LocaleContextHolder; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** */ +public class I18NReadEventListener implements PostLoadEventListener { + /** + * + */ + private static final long serialVersionUID = -7374424944903271183L; + + private Logger logger = LoggerFactory.getLogger(getClass()); + + private SessionFactoryImplementor sessionFactory; + @SuppressWarnings("rawtypes") + private Cache cache = + CacheBuilder.newBuilder().expireAfterAccess(30, TimeUnit.MINUTES).build(); + + public I18NReadEventListener(SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; + } + + @SuppressWarnings("rawtypes") + @Override + public void onPostLoad(PostLoadEvent event) { + Object entity = event.getEntity(); + Class clazz = entity.getClass(); + Collection fields = getLocalizedFields(clazz); + if (!fields.isEmpty()) + try (StatelessSession session = + sessionFactory.openStatelessSession(event.getSession().connection())) { + + Object id = getPKValue(clazz, entity); + + String language = LocaleContextHolder.getLocale().toString(); + + for (Field field : fields) { + List result = getTranslate(session, id, field, language); + if (!result.isEmpty()) field.set(entity, result.get(0)); + } + } catch (Exception e) { + logger.error("ReadEventListener error", e); + throw new IllegalStateException(); + } + } + + private List getTranslate(StatelessSession session, Object id, Field field, String language) { + String key = field + "_" + id + "_" + language; + List result = cache.getIfPresent(key); + if (result == null) { + I18NTranslate localized = field.getAnnotation(I18NTranslate.class); + String sql = + String.format( + "select %s from %s where %s = ? and languageCode = ?", + localized.column(), localized.table(), localized.id()); + SQLQuery query = session.createSQLQuery(sql); + query.setParameter(0, id); + query.setParameter(1, language); + result = query.list(); + logger.info("SQL: {}: {}", sql, result); + cache.put(key, result); + } + return result; + } + + public static Object getPKValue(Class clazz, Object entity) + throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Collection fields = getAllDeclaredFields(clazz); + for (Field field : fields) { + if (field.getAnnotation(Id.class) != null) { + field.setAccessible(true); + return field.get(entity); + } + } + for (Method method : clazz.getDeclaredMethods()) { + if (method.getAnnotation(Id.class) != null) { + return method.invoke(entity, null); + } + } + return null; + } + /** + * Returns the entity's @{@link I18NTranslate} fields. + * + *

These fields are made accessible. + */ + private static Collection getLocalizedFields(Class clazz) { + Collection fields = getAllDeclaredFields(clazz); + List localizedFields = new ArrayList<>(); + fields + .stream() + .filter(field -> field.getAnnotation(I18NTranslate.class) != null) + .forEach( + field -> { + field.setAccessible(true); + localizedFields.add(field); + }); + return localizedFields; + } + + private static Collection getAllDeclaredFields(Class clazz) { + List fields = new ArrayList<>(); + Collections.addAll(fields, clazz.getDeclaredFields()); + if (clazz.getSuperclass() != null) { + fields.addAll(getAllDeclaredFields(clazz.getSuperclass())); + } + return fields; + } +} diff --git a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslate.java b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslate.java new file mode 100644 index 0000000..49c7da6 --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslate.java @@ -0,0 +1,34 @@ +package org.ylhealth.ym.springtest.hibernate; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 利用 annotation 處理多國語系轉換 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface I18NTranslate { + /** + * 對應的欄位 + * @return + */ + String column() default ""; + + /** + * 對應的 id + * @return + */ + String id() default ""; + + /** + * 翻譯的表格 + * @return + */ + String table() default ""; + + + +} diff --git a/src/main/java/org/ylhealth/ym/springtest/hibernate/SqlServerDialectWithNvarchar.java b/src/main/java/org/ylhealth/ym/springtest/hibernate/SqlServerDialectWithNvarchar.java new file mode 100644 index 0000000..b9a641c --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/SqlServerDialectWithNvarchar.java @@ -0,0 +1,10 @@ +package org.ylhealth.ym.springtest.hibernate; + +import java.sql.Types; +import org.hibernate.dialect.SQLServer2012Dialect; + +public class SqlServerDialectWithNvarchar extends SQLServer2012Dialect { + public SqlServerDialectWithNvarchar() { + registerHibernateType(Types.NVARCHAR, 4000, "string"); + } +} diff --git a/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java b/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java new file mode 100644 index 0000000..1b53ffd --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java @@ -0,0 +1,6 @@ +package org.ylhealth.ym.springtest.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.ylhealth.ym.springtest.entity.CrmSystemL1basic; + +public interface CrmSystemL1basicRepo extends JpaRepository {} diff --git a/src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator b/src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator new file mode 100644 index 0000000..49ffa6f --- /dev/null +++ b/src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator @@ -0,0 +1 @@ +org.ylhealth.ym.springtest.hibernate.I18NInterceptor \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 36c4de5..08684d9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -19,7 +19,7 @@ spring: hibernate: show_sql: true format_sql: false - dialect: org.hibernate.dialect.SQLServer2012Dialect + dialect: org.ylhealth.ym.springtest.hibernate.SqlServerDialectWithNvarchar globally_quoted_identifiers: true temp: use_jdbc_metadata_defaults: false -- 2.26.2