Open SherryLang opened 7 years ago
获取EntityManager的实例,使用实例的方法来实现creatQuery
package com.xxxxx.repository.base.impl; import java.io.Serializable; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import com.xxxxx.repository.base.BasicRepository; import com.xxxxx.support.GenericsTool; /** * 对外相当于simpleJpaRepository的功能 * implements BasicRepository<T, ID>的部分可以不要,并且去掉@Override注解,相当于JPA的方法全部使用em自定义实现 */ public class BasicDAO<T, ID extends Serializable> implements BasicRepository<T, ID>{ @PersistenceContext protected EntityManager entityManager; private BasicRepository<T, ID> basicRepository; @SuppressWarnings("unchecked") private BasicRepository<T, ID> getBasicRepository() { if (null == basicRepository) { //通过反射,Class声明的范型参数的类型,用于实例化BasicRepositoryImpl对象,效果同Factory basicRepository = new BasicRepositoryImpl<T, ID>( (Class<T>) GenericsTool.getSuperClassGenricType(getClass()), entityManager); } return basicRepository; } @Override public List<T> findAll() { return getBasicRepository().findAll(); } @Override // ...(父类的方法全部Override一遍) }
其中用到的工具类方法如下:
package com.xxxxx.constant; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 反射转换对象什么的泛型参数类型 */ public class GenericsTool { private static final Logger log = LoggerFactory.getLogger(GenericsTool.class); /** * 通过反射,获得定义Class时声明的父类的范型参数的类型 * @param clazz 类对象 * @return 泛型类型 */ public static Class<?> getSuperClassGenricType(Class<?> clazz) { return getSuperClassGenricType(clazz, 0); } /** * 通过反射,获得定义Class时声明的父类的范型参数的类型 * @param clazz 类对象 * @param index 泛型位置,从0开始 * @return 泛型类型 */ public static Class<?> getSuperClassGenricType(Class<?> clazz, int index) { Type genType = clazz.getGenericSuperclass(); if (!(genType instanceof ParameterizedType)) { log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType"); return Object.class; } Type[] params = ((ParameterizedType)genType).getActualTypeArguments(); if (index >= params.length || index < 0) { log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length); return Object.class; } if (!(params[index] instanceof Class<?>)) { log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); return Object.class; } return (Class<?>)params[index]; } }
6月9日白天的代码 《深入实践Spring Boot》P83:自定义的接口必须在程序启动时装配,才能正常使用……
CccPageRepository 接口继承自JpaRepository,只要需要定义方法名(避免暴露函数体),并且使用@NoRepositoryBean注解:
package com.xxxx.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean; import com.xxxx.domain.CccPage; @NoRepositoryBean public interface CccPageRepository extends JpaRepository<CccPage, Long>{ List<CccPage> findByDocNo(String docNo); List<CccPage> searchCccByKeyword(String fieldName, String[] keywords); }
实现类内部定义私有的EntityManager实体,并且用@Autowired注入,用构造函数构造自身(包括父类):
package com.xxxx.repository; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.stereotype.Repository; import com.xxxx.domain.CccPage; @Repository public class CccPageRepositoryImpl extends SimpleJpaRepository<CccPage, Long> implements CccPageRepository { private String[] fields = { "model", "productName", "docNo", "remarks" }; //私有成员变量em private EntityManager em; @Autowired public CccPageRepositoryImpl (EntityManager em) { super(CccPage.class, em); this.em = em; } @SuppressWarnings("unchecked") public List<CccPage> findByDocNo(String docNo) { Query query = em.createQuery("from CccPage where docNo=:docNo"); query.setParameter("docNo", docNo); return query.getResultList(); } @SuppressWarnings("unchecked") public List<CccPage> searchCccByKeyword(String fieldName, String[] keywords) { StringBuilder sqlString = new StringBuilder(""); //隐藏函数体,主要功能是构造自定义的sqlString System.out.println(sqlString.toString()); Query query = em.createQuery(sqlString.toString()); List<CccPage> res = query.getResultList(); return res; } }
关键:基类BasicDAO 反射获取EntityManager实例
package com.xxxx.repository.base; import java.lang.reflect.Field; import javax.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; public interface BasicDAO { /** * 通过SimpleJpaRepository的em属性,反射获取EntityManager的实例 * * @return */ default EntityManager getEntityManager() { try { Field f = SimpleJpaRepository.class.getDeclaredField("em"); f.setAccessible(true); return (EntityManager) f.get(this); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { LoggerFactory.getLogger(BasicDAO.class) .error("not get current entityManager from reflect SimpleJpaRepository's field", e); } return null; } }
CccPageRepository 继承JpaRepository和基类BasicDAO
package com.xxxx.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean; import com.xxxx.domain.CccPage; import com.xxxx.repository.base.BasicDAO; /* 注意这里要继承BasicDAO!*/ @NoRepositoryBean public interface CccPageRepository extends JpaRepository<CccPage, Long>, BasicDAO { List<CccPage> findByDocNo(String docNo); List<CccPage> searchCccByKeyword(String fieldName, String[] keywords); }
实现类CccPageRepositoryImpl
package com.xxxx.repository; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.stereotype.Repository; import com.xxxx.domain.CccPage; @Repository public class CccPageRepositoryImpl extends SimpleJpaRepository<CccPage, Long> implements CccPageRepository { private String[] fields = { "model", "productName", "docNo", "remarks" }; /* * private EntityManager em; */ public CccPageRepositoryImpl (EntityManager em) { super(CccPage.class, em); } @SuppressWarnings("unchecked") public List<CccPage> findByDocNo(String docNo) { Query query = getEntityManager().createQuery("from CccPage where docNo=:docNo"); query.setParameter("docNo", docNo); return query.getResultList(); } @SuppressWarnings("unchecked") public List<CccPage> searchCccByKeyword(String fieldName, String[] keywords) { StringBuilder sqlString = new StringBuilder(""); //隐藏函数体,主要功能是构造自定义的sqlString System.out.println(sqlString.toString()); Query query = getEntityManager().createQuery(sqlString.toString()); List<CccPage> res = query.getResultList(); return res; } }
主要目的
获取EntityManager的实例,使用实例的方法来实现creatQuery
第一种实现:最原始的方式
其中用到的工具类方法如下:
第二种实现:Factory构造Bean,然而在接口中暴露了函数体
6月9日白天的代码 《深入实践Spring Boot》P83:自定义的接口必须在程序启动时装配,才能正常使用……
第三种实现:实现CccPageRepository接口,避免使用default修饰
CccPageRepository 接口继承自JpaRepository,只要需要定义方法名(避免暴露函数体),并且使用@NoRepositoryBean注解:
实现类内部定义私有的EntityManager实体,并且用@Autowired注入,用构造函数构造自身(包括父类):
第四种实现:反射获取em实例
关键:基类BasicDAO 反射获取EntityManager实例
CccPageRepository 继承JpaRepository和基类BasicDAO
实现类CccPageRepositoryImpl