diff --git a/pom.xml b/pom.xml index 42607f9be9228edbee5ddb80711c1f57c1190355..6596eff1e893cae012820091791bd12b5e6788ba 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,23 @@ guava 23.0 + + + net.sf.ehcache + ehcache + + + + + org.hibernate + hibernate-ehcache + + + net.sf.ehcache + ehcache-core + + + diff --git a/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java b/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java index a0b42ee20ce4f1b29e0a5b94441d19412b584f7b..2ef21278118f1627bae2cff15bc3db41d03ae72c 100644 --- a/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java +++ b/src/main/java/org/ylhealth/ym/springtest/controller/I18NTestController.java @@ -1,6 +1,9 @@ package org.ylhealth.ym.springtest.controller; +import java.util.List; +import java.util.Locale; import javax.inject.Inject; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.ylhealth.ym.springtest.entity.CrmSystemL1basic; @@ -10,8 +13,27 @@ import org.ylhealth.ym.springtest.repo.CrmSystemL1basicRepo; public class I18NTestController { @Inject CrmSystemL1basicRepo dao; - @GetMapping("/i18n/Test1") - public CrmSystemL1basic test1() { + + @GetMapping("/i18n/Test11") + public CrmSystemL1basic test11() { + return dao.findOne("01"); + } + @GetMapping("/i18n/Test12") + public CrmSystemL1basic test12() { + return dao.findByGroupCode("01"); + } + @GetMapping("/i18n/Test13") + public List test13() { + return dao.findAll(); + } + @GetMapping("/i18n/Test21") + public CrmSystemL1basic test21() { + LocaleContextHolder.setLocale(Locale.SIMPLIFIED_CHINESE); return dao.findOne("01"); } + @GetMapping("/i18n/Test22") + public CrmSystemL1basic test22() { + LocaleContextHolder.setLocale(Locale.SIMPLIFIED_CHINESE); + return dao.findByGroupCode("01"); + } } diff --git a/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java b/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java index 6ef2bc56065e74e2abf09b1a99c6107d7b2f63c8..628371ff033a61c07c6bfa761adacb8fed6794f3 100644 --- a/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java +++ b/src/main/java/org/ylhealth/ym/springtest/entity/CrmSystemL1basic.java @@ -2,6 +2,7 @@ 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.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -9,20 +10,27 @@ import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Nationalized; import org.ylhealth.ym.springtest.hibernate.I18NTranslate; +import org.ylhealth.ym.springtest.hibernate.I18NTranslates; import com.fasterxml.jackson.annotation.JsonIgnore; /** CrmSystemL1basic generated by hbm2java */ @Entity +@Cacheable @Table(name = "CRM_SystemL1Basic") +@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@I18NTranslates( + id = "groupCode", + table = "CRM_SystemL1Basic_translation", + mapping = {@I18NTranslate(column = "groupName", field = "groupName")}) 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; diff --git a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java index dcde4586f6b68ac0d93648fb493c1059eef2fdb7..cb681348553d7653a105c820f1ed6d4e04d5558f 100644 --- a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NReadEventListener.java @@ -6,7 +6,9 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import javax.persistence.Id; import org.hibernate.SQLQuery; @@ -14,6 +16,7 @@ import org.hibernate.StatelessSession; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEventListener; +import org.hibernate.transform.Transformers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.i18n.LocaleContextHolder; @@ -22,17 +25,16 @@ 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(); + private Cache cache = + CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.MINUTES).build(); public I18NReadEventListener(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; @@ -43,45 +45,83 @@ public class I18NReadEventListener implements PostLoadEventListener { public void onPostLoad(PostLoadEvent event) { Object entity = event.getEntity(); Class clazz = entity.getClass(); - Collection fields = getLocalizedFields(clazz); - if (!fields.isEmpty()) + + I18NTranslates i18n = entity.getClass().getAnnotation(I18NTranslates.class); + if (i18n != null) { 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)); + Map translate = getTranslate(session, i18n, entity, clazz); + if (translate != null && !translate.isEmpty()) { + Field[] fields = clazz.getDeclaredFields(); + for (I18NTranslate i18nTranslate : i18n.mapping()) { + for (Field field : fields) { + if (field.getName().equals(i18nTranslate.field())) { + field.setAccessible(true); + field.set(entity, translate.get(i18nTranslate.column())); + break; + } + } + } } } catch (Exception e) { logger.error("ReadEventListener error", e); throw new IllegalStateException(); } + } + } + + /** + * @param i18n + * @param entity + * @param clazz + * @return + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + private Map getTranslate( + StatelessSession session, I18NTranslates i18n, Object entity, Class clazz) + throws IllegalAccessException, InvocationTargetException { + Object id = getPKValue(clazz, entity); + String language = LocaleContextHolder.getLocale().toString(); + String table = i18n.table(); + String idColumn = i18n.id(); + + Map> translateTable = + translateTable(session, table, language, i18n.mapping(), idColumn); + + return translateTable.get(id); } - private List getTranslate(StatelessSession session, Object id, Field field, String language) { - String key = field + "_" + id + "_" + language; - List result = cache.getIfPresent(key); + private Map> translateTable( + StatelessSession session, + String table, + String language, + I18NTranslate[] mapping, + String idColumn) { + String key = "Table:" + table + "_" + language; + Map> 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); + logger.trace("translate query, table: {}", table); + String sql = String.format("select * from %s where languageCode = ?", table); + SQLQuery query1 = session.createSQLQuery(sql); + query1.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); + query1.addScalar(idColumn); + for (I18NTranslate i18nTranslate : mapping) { + query1.addScalar(i18nTranslate.column()); + } + query1.setParameter(0, language); + List> result1 = query1.list(); + result = new HashMap<>(); + for (Map map : result1) { + result.put(map.get(idColumn), map); + } cache.put(key, result); } return result; } - public static Object getPKValue(Class clazz, Object entity) + private Object getPKValue(Class clazz, Object entity) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Collection fields = getAllDeclaredFields(clazz); for (Field field : 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 index 49c7da61546340f7f7f0f116a392861f16f77e0b..38683f3b6409f9e73d8a0d9c56eba6a4f54fa340 100644 --- a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslate.java +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslate.java @@ -9,26 +9,17 @@ import java.lang.annotation.Target; * 利用 annotation 處理多國語系轉換 */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD}) +@Target({ElementType.TYPE}) public @interface I18NTranslate { /** - * 對應的欄位 + * Entity class 的欄位(注意大小寫) * @return */ - String column() default ""; - + String field(); /** - * 對應的 id + * 翻譯表格對應的欄位 * @return */ - String id() default ""; - - /** - * 翻譯的表格 - * @return - */ - String table() default ""; - - + String column(); } diff --git a/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslates.java b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslates.java new file mode 100644 index 0000000000000000000000000000000000000000..93b8e821154c058c05c83789caa04af51e12b6a8 --- /dev/null +++ b/src/main/java/org/ylhealth/ym/springtest/hibernate/I18NTranslates.java @@ -0,0 +1,29 @@ +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; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface I18NTranslates { + /** + * 翻譯表格對應的資料 id 欄位 + * + * @return + */ + String id(); + + /** + * 翻譯的表格 + * + * @return + */ + String table(); + + /** + * 要翻譯的資料欄位對應 + */ + I18NTranslate[] mapping(); +} diff --git a/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java b/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java index 1b53ffd743fbfef46ac32d254374efa8451657c6..b497c90999f8c1220c0b1d8f8d9c8ac3cf2f9690 100644 --- a/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java +++ b/src/main/java/org/ylhealth/ym/springtest/repo/CrmSystemL1basicRepo.java @@ -1,6 +1,12 @@ package org.ylhealth.ym.springtest.repo; +import javax.persistence.QueryHint; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.QueryHints; import org.ylhealth.ym.springtest.entity.CrmSystemL1basic; -public interface CrmSystemL1basicRepo extends JpaRepository {} +public interface CrmSystemL1basicRepo extends JpaRepository { + + @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") }) + CrmSystemL1basic findByGroupCode(String groupCode); +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 08684d98c3b4105bc4f3b8bfbbb54484e3d520b2..7b58a034603cc422d850cb5806f7e146faa736bb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,6 +21,10 @@ spring: format_sql: false dialect: org.ylhealth.ym.springtest.hibernate.SqlServerDialectWithNvarchar globally_quoted_identifiers: true + cache.use_second_level_cache: true + cache.use_query_cache: true + cache.region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory + generate_statistics: true temp: use_jdbc_metadata_defaults: false diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml new file mode 100644 index 0000000000000000000000000000000000000000..c9b339038bc20b594ef1005864ebe1c230aaa5f6 --- /dev/null +++ b/src/main/resources/ehcache.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + +