Spring-基于JPA的动态SQL执⾏器JPA动态SQL执⾏
通过JPA的EntityManager 实现SQL的执⾏。
public class DaoProxySupport {
private EntityManager em;
public DaoProxySupport(EntityManager em){
< = em;
}
public<T> List<T>search(String sql, Map<String, Object> condition, Class<T> clazz){
Query query =ateNativeQuery(sql, clazz);
if(condition != null){
for(String key : condition.keySet()){
if(SqlParserUtil.hasParam(sql, key))
query.setParameter(key, (key));
}
}
List<T> list = ResultList();
return list;
}
定义注解
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicSqlDao {
String value()default"";// 默认使⽤class名对应的SQL⽂件
}
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SqlKey {
String value()default"";//接⼝对应的sqlKey
Class<?>beanClass();
}
使⽤⽅式如下:
@DynamicSqlDao
public interface MySqlDao {
@SqlKey("GET_LIST",beanClass = TableA.class)
List<TableA>getList(Map<String, Object> condition);
}
对应SQL⽂件
GET_LIST=
SELECT*FROM table_a
WHERE status=1
AND(<if key="code">code like :code OR name like :code</if>)
动态sql的实现,防mybatis,根据map中的key是否存在决定是否执⾏其中的SQL。
如果不存在,直接转化为1=1即可。
SQL⽂件的加载及解析实现略过。
扫描SqlDao并注⼊到IOC容器
通过实现BeanDefinitionRegistryPostProcessor扫描指定包路径,并把BeanDefinition注册到BeanDefinitionRegistry 。
public class DaoDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {
// SqlDao扫描的包路径
private String sacnBasePackage ="";
public String getSacnBasePackage(){
return sacnBasePackage;
}
public void setSacnBasePackage(String sacnBasePackage){
this.sacnBasePackage = sacnBasePackage;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)throws BeansException {
// 获取需要代理的接⼝的clazz列表
Set<Class<?>> beanClazzs =scannerPackages(sacnBasePackage);
for(Class<?> beanClazz : beanClazzs){
BeanDefinitionBuilder builder = icBeanDefinition(beanClazz);
GenericBeanDefinition definition =(GenericBeanDefinition) RawBeanDefinition();
// 加载接⼝对应的SQL⽂件
// 在这⾥,我们可以给该对象的属性注⼊对应的实例。
// ⽐如mybatis,就在这⾥注⼊了dataSource和sqlSessionFactory,
// 注意,如果采⽤PropertyValues()⽅式的话,
// 类似PropertyValues().add("interfaceType", beanClazz);
// 则要求在FactoryBean(本应⽤中即ServiceFactory)提供setter⽅法,否则会注⼊失败
// 如果采⽤ConstructorArgumentValues(),
// 则FactoryBean中需要提供包含该属性的构造⽅法,否则会注⼊失败
// PropertyValues().add("interfaceClass",
// BeanClassName());
// 注意:这⾥的BeanClass是⽣成Bean实例的⼯⼚,不是Bean本⾝。
// FactoryBean是⼀种特殊的Bean,其返回的对象不是指定类的⼀个实例,
/
/ 其返回的是该⼯⼚Bean的getObject⽅法所返回的对象。
definition.setBeanClass(DaoFactory.class);
// 采⽤的是byType⽅式注⼊,类似的还有byName等
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
private static final String DEFAULT_RESOURCE_PATTERN ="**/*.class";
jpa mybatisprivate MetadataReaderFactory metadataReaderFactory;
/**
* 根据包路径获取包及⼦包下的所有类
*
* @param basePackage basePackage
* @return Set<Class<?>> Set<Class<?>>
* @throws IOException
* @throws ClassNotFoundException
*/
private Set<Class<?>>scannerPackages(String basePackage)throws BeansException {
Set<Class<?>> set =new LinkedHashSet<>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage)+'/'+ DEFAULT_RESOU
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage)+'/'+ DEFAULT_RESOU RCE_PATTERN;
Resource[] resources;
try{
resources =Resources(packageSearchPath);
for(Resource resource : resources){
if(resource.isReadable()){
MetadataReader metadataReader =MetadataReader(resource);
String className = ClassMetadata().getClassName();
Class<?> clazz = Class.forName(className);
// 如果不是接⼝,跳过
if(!clazz.isInterface()){
continue;
}
// 如果没有DynamicSqlDao的注解,跳过
if(!clazz.isAnnotationPresent(DynamicSqlDao.class)){
continue;
}
set.add(clazz);
}
}
}catch(Exception e){
throw new FatalBeanException("扫描Sql Dao包下接⼝出错。", e);
}
return set;
}
protected String resolveBasePackage(String basePackage){
Environment().resolveRequiredPlaceholders(basePackage));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throws BeansException {
}
private ResourcePatternResolver resourcePatternResolver;
private ApplicationContext applicationContext;
@Override
public void setResourceLoader(ResourceLoader resourceLoader){
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
this.applicationContext = applicationContext;
}
private Environment getEnvironment(){
Environment();
}
}
动态代理
DaoFactory是SqlDao的⼯⼚类,实现FactoryBean的接⼝。
负责在getObject产⽣具体的实例。具体的实例是⼀个JDK的动态代理类。
通过 @PersistenceContext private EntityManager em; 注⼊SQL的执⾏器。
public class DaoFactory<T>implements FactoryBean<T>{
private Class<T> interfaceType;
@PersistenceContext
private EntityManager em;
public DaoFactory(Class<T> interfaceType){
this.interfaceType = interfaceType;
}
@SuppressWarnings("unchecked")
@Override
public T getObject()throws Exception {
// 创建接⼝对应的实例,便于注⼊到spring容器中
return(T) ClassLoader(),new Class[]{ interfaceType },new DaoProxy<>(,this.interfaceType)); }
@Override
public Class<T>getObjectType(){
return interfaceType;
}
@Override
public boolean isSingleton(){
return true;
}
}
动态代理的真正执⾏在DaoProxy中。
public class DaoProxy<T>implements InvocationHandler {
private Logger logger = Logger(DaoProxy.class);
private Class<T> interfaceType;
private EntityManager em;
public DaoProxy(EntityManager em, Class<T> interfaceType){
< = em;
this.interfaceType = interfaceType;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
// 如果是Object对象的⽅法,直接调⽤
if(Object.class.DeclaringClass())){
return method.invoke(this, args);
}
logger.info("调⽤的接⼝类:{}", interfaceType);
Object result = null;
if(method.isAnnotationPresent(SqlKey.class)){
SqlKey key = Annotation(SqlKey.class);
String sqkKey = key.value();
Class<?> beanCalzz = key.beanClass();
logger.info("调⽤的⽅法:{}", method);
// 取得Condition对象
Map<String, Object> condition =new HashMap<String, Object>();
// 取得条件参数
for(int i =0; i < args.length; i++){
if(args[i]instanceof Map){
condition =(Map<String, Object>) args[i];
}
}
logger.info("Sql参数:{}", condition);
// 执⾏SQL
DaoProxySupport support =new );
String strSql = Instance().getSql(interfaceType, sqkKey, condition);
result = support.search(strSql, condition, beanCalzz);
}
return result;
}
}
以上,开发时像mybatis⼀样,编写接⼝和对应SQL,即可实现简单的动态SQL的执⾏了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论