diff --git a/pom.xml b/pom.xml index 3bd577a6ac85a1d682d6f099dca9ffb05bcaaf8b..42607f9be9228edbee5ddb80711c1f57c1190355 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 0000000000000000000000000000000000000000..a0b42ee20ce4f1b29e0a5b94441d19412b584f7b --- /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 cd4eb3d1e8df7d376f0e17a4681f361c0cadf692..30f66324c4e8ee061b602b90db2e6eafe651e851 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 0000000000000000000000000000000000000000..6ef2bc56065e74e2abf09b1a99c6107d7b2f63c8 --- /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 0000000000000000000000000000000000000000..21fefdc431313ee7e493550e356ee6496bce6346 --- /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 0000000000000000000000000000000000000000..dcde4586f6b68ac0d93648fb493c1059eef2fdb7 --- /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 0000000000000000000000000000000000000000..49c7da61546340f7f7f0f116a392861f16f77e0b --- /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 0000000000000000000000000000000000000000..b9a641c57834921e5d414524b663bd58d70c9662 --- /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 0000000000000000000000000000000000000000..1b53ffd743fbfef46ac32d254374efa8451657c6 --- /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 0000000000000000000000000000000000000000..49ffa6f74cdda3b88651900f82e06cd5f9d0d69f --- /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 36c4de50c907122c2cff2e5e04a6381a574e196d..08684d98c3b4105bc4f3b8bfbbb54484e3d520b2 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