JPA动态SQL执行

通过JPA的EntityManager 实现SQL的执行。

public class DaoProxySupport {private EntityManager em;public DaoProxySupport(EntityManager em) {this.em = em;}public <T> List<T> search(String sql, Map<String, Object> condition, Class<T> clazz) {Query query = this.em.createNativeQuery(sql, clazz);if (condition != null) {for (String key : condition.keySet()) {if (SqlParserUtil.hasParam(sql, key))query.setParameter(key, condition.get(key));}}List<T> list = query.getResultList();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 "";  //接口对应的sqlKeyClass<?> 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;}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 获取需要代理的接口的clazz列表Set<Class<?>> beanClazzs = scannerPackages(sacnBasePackage);for (Class<?> beanClazz : beanClazzs) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();// 加载 接口对应的SQL文件DaoSqlReader.getInstance().loadSql(beanClazz);// 在这里,我们可以给该对象的属性注入对应的实例。// 比如mybatis,就在这里注入了dataSource和sqlSessionFactory,// 注意,如果采用definition.getPropertyValues()方式的话,// 类似definition.getPropertyValues().add("interfaceType", beanClazz);// 则要求在FactoryBean(本应用中即ServiceFactory)提供setter方法,否则会注入失败// 如果采用definition.getConstructorArgumentValues(),// 则FactoryBean中需要提供包含该属性的构造方法,否则会注入失败// definition.getPropertyValues().add("interfaceClass",// definition.getBeanClassName());definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);// 注意:这里的BeanClass是生成Bean实例的工厂,不是Bean本身。// FactoryBean是一种特殊的Bean,其返回的对象不是指定类的一个实例,// 其返回的是该工厂Bean的getObject方法所返回的对象。definition.setBeanClass(DaoFactory.class);// 采用的是byType方式注入,类似的还有byName等definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);registry.registerBeanDefinition(beanClazz.getSimpleName(), definition);}}private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";private 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_RESOURCE_PATTERN;Resource[] resources;try {resources = this.resourcePatternResolver.getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);String className = metadataReader.getClassMetadata().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) {return ClassUtils.convertClassNameToResourcePath(this.getEnvironment().resolveRequiredPlaceholders(basePackage));}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}private ResourcePatternResolver resourcePatternResolver;private ApplicationContext applicationContext;@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}private Environment getEnvironment() {return applicationContext.getEnvironment();}}

动态代理

DaoFactory是SqlDao的工厂类,实现FactoryBean的接口。
负责在getObject产生具体的实例。具体的实例是一个JDK的动态代理类。
通过 @PersistenceContext private EntityManager em; 注入SQL的执行器。


public class DaoFactory<T> implements FactoryBean<T> {private Class<T> interfaceType;@PersistenceContextprivate EntityManager em;public DaoFactory(Class<T> interfaceType) {this.interfaceType = interfaceType;}@SuppressWarnings("unchecked")@Overridepublic T getObject() throws Exception {// 创建接口对应的实例,便于注入到spring容器中return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[] { interfaceType }, new DaoProxy<>(this.em, this.interfaceType));}@Overridepublic Class<T> getObjectType() {return interfaceType;}@Overridepublic boolean isSingleton() {return true;}
}

动态代理的真正执行在DaoProxy中。

public class DaoProxy<T> implements InvocationHandler {private Logger logger = LoggerFactory.getLogger(DaoProxy.class);private Class<T> interfaceType;private EntityManager em;public DaoProxy(EntityManager em, Class<T> interfaceType) {this.em = em;this.interfaceType = interfaceType;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 如果是Object对象的方法,直接调用if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}logger.info("调用的接口类:{}", interfaceType);Object result = null;if (method.isAnnotationPresent(SqlKey.class)) {SqlKey key = method.getAnnotation(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);// 执行SQLDaoProxySupport support = new DaoProxySupport(this.em);String strSql = DaoSqlReader.getInstance().getSql(interfaceType, sqkKey, condition);result = support.search(strSql, condition, beanCalzz);}return result;}
}

以上,开发时像mybatis一样,编写接口和对应SQL,即可实现简单的动态SQL的执行了。

Spring - 基于JPA的动态SQL执行器相关推荐

  1. MyBatis之基于XML的动态SQL

    先说下我的梦想,大学的时候一直想着是能开店卖胡辣汤,到目前依然还是我的梦想,上周一家出版社联系我问我有没有时间可以合作出书,这也是我的梦想之一,想了想还是放弃了,至少觉得目前不行,毕竟工作还不到五年, ...

  2. JPA的动态SQL IF判断

    最近需求复杂的sql,使用JPA的ORM不太好解决 学习了JPA的IF判断,来处理 @Query(value = "select pr.*\n" +"from proje ...

  3. SpringData JPA @Query动态SQL语句

    前言 这次有个需求,需要动态的sql语句去查询,但是@Query正常情况下SQL语句是写死的,在查找了很多资料后,想到了一个好的解决办法 思路 利用MYSQL的判断来拼接SQL语句 实现 先上代码 @ ...

  4. Spring Data Jpa 不打印sql参数

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-bo ...

  5. jpa多表联查动态_Spring Data JPA实现动态多表关联查询

    在面向关系型数据库(例如:MySQL)的查询中,动态的.多表关联的查询属于比较复杂的情况.所以,我们只要掌握了这种复杂的查询,当面对其他查询情况时,就能做到胸有成竹. 在java工程中,目前我所了解到 ...

  6. Spring Data JPA使用必备(二):Spring Data JPA方法命名规则实现SQL自动生成

    Spring data JPA是一个好东西,但是对于很多习惯于写SQL,直接怼数据库的人来说,这个真的用不习惯,还被一致认为是一个不易于程序员发展的技术.因为JPA提供了标准的封装,在操作数据库的时候 ...

  7. springboot 集成jpa_基于Spring Boot+JPA Restful 风格的数据

    第一章 Restful简介 Restful是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服 务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次, ...

  8. springboot jpa sql打印_SpringBoot集成Spring Data JPA以及读写分离

    相关代码:github OSCchina JPA是什么 JPA(Java Persistence API)是Sun官方提出的Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具 来管理 ...

  9. spring data jpa封装specification实现简单风格的动态查询

    github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...

  10. dao层动态sql利器!数据持久化jpa插件spring-data-jpa-extra?Fenix!

    把hibernate.mybatis都整合到一个项目中,有没有觉得臃肿繁琐?!        我们知道,dao层,会用数据持久化框架,如hibernate.mybatis等,他们都实现了jpa规范.几 ...

最新文章

  1. MAT之PSO:利用PSO算法优化二元函数,寻找最优个体适应度
  2. TypeScript 函数类型参数的用法举例
  3. java普通类方法的区别是什么_java – 普通接口类和只有抽象方法的抽象类之间有什么区别吗?...
  4. 2018 蓝桥杯省赛 B 组模拟赛(一) 封印之门+最短路径之Floyd
  5. nssl1164-观察【平衡树,LCA】
  6. elementui树状菜单tree_vue.js+element-ui做出菜单树形结构
  7. node.js 安装使用http-server
  8. iOS原生实现二维码扫描
  9. c++语言表白超炫图形_R语言统计与绘图:组合图形布局
  10. css多行文本溢出显示省略号(兼容ie)
  11. check的用法java_Java ChronoField checkValidValue()用法及代码示例
  12. 第七章 线程的调度、优先级和亲缘性(4)
  13. 软件公司产品营销大数据分析(上)
  14. 【谈天说地】诋毁中华文化的三大谎言(之三)
  15. 4. Browser 对象 - Navigator 对象(2)
  16. 有符号整型的数据范围为什么负数比正数多一个
  17. 尚学堂视频笔记三:容器
  18. 置信区间、P值那点事
  19. linux 截取某一段时间的日志,存储到另一个文件中
  20. Python求圆的面积和周长

热门文章

  1. 12.9日个人工作总结
  2. 系统安全之数据加密解密
  3. Spring和ThreadLocal
  4. UVa1592 数据库(摘)
  5. 7. Swift 基于Xmpp和openfire实现一个简单的登录注册
  6. netbeans-xdebug
  7. [经验]自定义ASP.NET服务器控件属性的状态不能保存的问题
  8. 1.Jenkins 2 权威指南 --- 简介
  9. 23. PHP include and require 文件
  10. 第014讲 CSS 定位