Spring - 基于JPA的动态SQL执行器
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执行器相关推荐
- MyBatis之基于XML的动态SQL
先说下我的梦想,大学的时候一直想着是能开店卖胡辣汤,到目前依然还是我的梦想,上周一家出版社联系我问我有没有时间可以合作出书,这也是我的梦想之一,想了想还是放弃了,至少觉得目前不行,毕竟工作还不到五年, ...
- JPA的动态SQL IF判断
最近需求复杂的sql,使用JPA的ORM不太好解决 学习了JPA的IF判断,来处理 @Query(value = "select pr.*\n" +"from proje ...
- SpringData JPA @Query动态SQL语句
前言 这次有个需求,需要动态的sql语句去查询,但是@Query正常情况下SQL语句是写死的,在查找了很多资料后,想到了一个好的解决办法 思路 利用MYSQL的判断来拼接SQL语句 实现 先上代码 @ ...
- Spring Data Jpa 不打印sql参数
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-bo ...
- jpa多表联查动态_Spring Data JPA实现动态多表关联查询
在面向关系型数据库(例如:MySQL)的查询中,动态的.多表关联的查询属于比较复杂的情况.所以,我们只要掌握了这种复杂的查询,当面对其他查询情况时,就能做到胸有成竹. 在java工程中,目前我所了解到 ...
- Spring Data JPA使用必备(二):Spring Data JPA方法命名规则实现SQL自动生成
Spring data JPA是一个好东西,但是对于很多习惯于写SQL,直接怼数据库的人来说,这个真的用不习惯,还被一致认为是一个不易于程序员发展的技术.因为JPA提供了标准的封装,在操作数据库的时候 ...
- springboot 集成jpa_基于Spring Boot+JPA Restful 风格的数据
第一章 Restful简介 Restful是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服 务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次, ...
- springboot jpa sql打印_SpringBoot集成Spring Data JPA以及读写分离
相关代码:github OSCchina JPA是什么 JPA(Java Persistence API)是Sun官方提出的Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具 来管理 ...
- spring data jpa封装specification实现简单风格的动态查询
github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...
- dao层动态sql利器!数据持久化jpa插件spring-data-jpa-extra?Fenix!
把hibernate.mybatis都整合到一个项目中,有没有觉得臃肿繁琐?! 我们知道,dao层,会用数据持久化框架,如hibernate.mybatis等,他们都实现了jpa规范.几 ...
最新文章
- MAT之PSO:利用PSO算法优化二元函数,寻找最优个体适应度
- TypeScript 函数类型参数的用法举例
- java普通类方法的区别是什么_java – 普通接口类和只有抽象方法的抽象类之间有什么区别吗?...
- 2018 蓝桥杯省赛 B 组模拟赛(一) 封印之门+最短路径之Floyd
- nssl1164-观察【平衡树,LCA】
- elementui树状菜单tree_vue.js+element-ui做出菜单树形结构
- node.js 安装使用http-server
- iOS原生实现二维码扫描
- c++语言表白超炫图形_R语言统计与绘图:组合图形布局
- css多行文本溢出显示省略号(兼容ie)
- check的用法java_Java ChronoField checkValidValue()用法及代码示例
- 第七章 线程的调度、优先级和亲缘性(4)
- 软件公司产品营销大数据分析(上)
- 【谈天说地】诋毁中华文化的三大谎言(之三)
- 4. Browser 对象 - Navigator 对象(2)
- 有符号整型的数据范围为什么负数比正数多一个
- 尚学堂视频笔记三:容器
- 置信区间、P值那点事
- linux 截取某一段时间的日志,存储到另一个文件中
- Python求圆的面积和周长