Hibernate 离线对象构建通用查询
1.业务场景
当下主系统衍生子业务系统已经成为常态,像京东的物流和金融,阿里的支付宝和淘宝。
子业务系统需要对主系统的资源进行访问,这里的资源我具体化为数据库数据,但日常业务中可能不只是数据。
抽象服务给子业务系统进行调用访问? 各种各样子业务系统可能会出现千奇百怪的需求,主系统只有不断增加服务去拥抱变化。
创建多个数据库子账号给子业务系统?无法统一监控,而且很容易导致数据库瓶颈,到时候就需要加服务器资源。
权衡利弊和自己技术栈之后开始实践之路,大体目标:
a.通用方式获取主系统数据,且新增需求时主系统访问方式依然不变,在对应的子业务系统进行数据的处理;
b.进行统一监控,把控对外数据,收集日志并进行数据分析和挖掘;
2.技术细节
核心为 Hibernate DetachedCriteria 离线查询对象,对于 DetachedCriteria 我这里就不说了请自行百度。
当下 JavaEE 服务方式有很多,restful/soap/spring http invoker 等等,看你项目具体的服务框,这里我不赘述。
我将自身项目中抽取中该场景关键代码,剔除繁琐的业务代码,展示 hiber 通用查询带来的便利性和可扩展性。
实例中服务采用 cxf webservice,spring 做为托管容器,Base64 编码查询条件进行参数的传递。
主系统服务核心代码:
@Component
@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class CommonQueryWsImpl implements CommonQueryWs { @Resource
private SessionFactory sessionFactory; @Override
@Transactional(readOnly = true)
public String getPo(String poName, List<String> andList) {
DetachedCriteria detachedCriteria = getDetachedCriteria(poName, andList); Object result = detachedCriteria.getExecutableCriteria(sessionFactory.getCurrentSession()).setMaxResults(1).uniqueResult();
return ObjectUtil.isNull(result) ? "{}" : JSONUtil.toJsonStr(result);
} @Override
@Transactional(readOnly = true)
public String listPo(String poName, List<String> andList) {
DetachedCriteria detachedCriteria = getDetachedCriteria(poName, andList); List list = detachedCriteria.getExecutableCriteria(sessionFactory.getCurrentSession()).list();
return JSONUtil.toJsonStr(list);
} /**
* 获取 hiber 离线查询对象
*
* @param poName biber 实体对象
* @param andList andSql 列表
* @return 离线查询对象
*/
private DetachedCriteria getDetachedCriteria(String poName, List<String> andList) {
DetachedCriteria detachedCriteria = DetachedCriteria.forEntityName(poName); String[] split;
for (String andStr : andList) {
split = Base64.decodeStr(andStr).split("#");
String andSql = split[0];
String andParam = split[1]; if (andParam.contains(",")) {
String[] andParams = andParam.split(",");
Type[] types = new Type[andParams.length];
for (int i = 0; i < types.length; i++) {
types[i] = StringType.INSTANCE;
} detachedCriteria.add(Restrictions.sqlRestriction(andSql, andParams, types));
} else {
detachedCriteria.add(Restrictions.sqlRestriction(andSql, andParam, StringType.INSTANCE));
}
}
return detachedCriteria;
}
}
主系统服务端会用 spring 托管 hiber 所需实体类,配置基于注解的声明式事务管理。
还有种基于正则方法名进行拦截的事务管理,底层都是 AOP 方式,在拦截前后进行事务分配和回滚。
<!--激活启用基于 @Transactional 的声明式事务管理方式-->
<tx:annotation-driven/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_ids?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="111111"/>
</bean> <!-- 配置hibernate session工厂,需添加 spring-orm -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property> <!-- 自动扫描注解方式配置的hibernate类文件 -->
<property name="packagesToScan">
<list>
<value>com.rambo.**.po</value>
</list>
</property>
</bean> <!-- 配置事务管理器 -->
<bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
最后在服务端 web.xml 中先启动 spring 容器后初始化 cxf 服务。
<listener>
<description>上下文监听</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-cxf-context.xml
classpath:spring-hiber-context.xml
</param-value>
</context-param> <servlet>
<description>Apache CXF WebService 服务</description>
<servlet-name>CXF</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXF</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
到此,服务端核心部分代码和配置都已结束,其中有很多可变点。
a. 数据库连接我实例中使用了简单的 jdbc 的方式,当然你可以使用连接池像 dbcp/c3p0/druid等等;
b. 数据库配置作为可变配置,实例中硬编码到了 xml 当中,当然是不可取的行为,可以抽取出来到配置文件中;
3.调用姿势
服务抽象完且可以正常启动,就可以进行服务的调用,实例中我使用了 cxf 作为服务的表现形式(可以生成客户端代码或直接在 spring 中配置)。
如果你自身项目中在使用其他服务方式,那么你也很清楚服务的调用方式,正确访问服务后还需要对服务进行封装给子业务系统使用。
@Override
public <T> T getPo(Class<T> t, Map<String, String> andParamMap) {
try {
String retStr = recivePo(t, andParamMap, "get");
return JSONUtil.toBean(retStr, t);
} catch (Exception e) {
logger.error("获取单个通用查询业务数据实体表时发生错误:{}", e.toString());
e.printStackTrace();
}
return null;
} @Override
public <T> List<T> listPo(Class<T> t, Map<String, String> andParamMap) {
try {
JSONArray poJa = new JSONArray(recivePo(t, andParamMap, "list"));
logger.info("获取多个通用查询业务数据实体表 json 对象数量:{}", poJa.size()); poJa.removeIf(next -> ObjectUtil.isNull(next) || "null".equalsIgnoreCase(next.toString()));
logger.info("进行过滤数据后 json 对象数量:{}", poJa.size());
return poJa.toList(t);
} catch (Exception e) {
logger.error("获取多个通用查询业务数据实体表时发生错误:{}", e.toString());
e.printStackTrace();
}
return null;
} /**
* 调用远程 ws 服务获取通用数据
*
* @param t 要获取的实体 po
* @param andParamMap 参数 map
* @return 通用数据 json
*/
private String recivePo(Class t, Map<String, String> andParamMap, String method) throws Exception {
Assert.notNull(t);
Assert.notNull(andParamMap); String mappingUrl = "http://localhost:4042/hiber-common-query-server/ws/CommonQueryWs?wsdl";
String po;
if ("get".equals(method)) {
po = getService(mappingUrl).getPo(t.getName(), handleParamList(andParamMap));
} else {
po = getService(mappingUrl).listPo(t.getName(), handleParamList(andParamMap));
} Assert.notEmpty(po);
logger.info("调用地址:{},获取表:{}", mappingUrl, t.getName());
logger.info("返回的对象 json:{}", po);
return po;
}
子业务系统可独立依赖 api jar, 使用 spring 托管服务进行注入或者也可以简单粗暴的 new 一个进行调用。
@Test
public void testGetCommQueryPo() throws Exception {
CommonQueryServiceImpl commonQueryService = new CommonQueryServiceImpl(); SysUserPO sysUserPO = commonQueryService.getPo(SysUserPO.class, MapUtil.of("uuid = ?", "966364ac90e74fba8340a3aa9992ced1"));
Assert.notNull(sysUserPO);
} @Test
public void testGetCommQueryPo2() throws Exception {
CommonQueryServiceImpl commonQueryService = new CommonQueryServiceImpl();
HashMap<String, String> andParamMap = new LinkedHashMap<>();
andParamMap.put("(phone = ? or email = ?)", "18745962314,riceshop@live.cn"); List<SysUserPO> sysUserPOList = commonQueryService.listPo(SysUserPO.class, andParamMap);
Assert.notNull(sysUserPOList);
} /**
* Method: listPo(Class<T> t, Map<String, String> andParamMap)
*/
@Test
public void testListCommQuery() throws Exception {
CommonQueryServiceImpl commonQueryService = new CommonQueryServiceImpl(); HashMap<String, String> andParamMap = new LinkedHashMap<>();
andParamMap.put("name like ? ", "%黄%");
andParamMap.put("date_format(create_date,'%Y%m%d') >= ? ", "201809");
andParamMap.put("photo = ? order by update_date desc", "1"); List<SysUserPO> sysUserPOList = commonQueryService.listPo(SysUserPO.class, andParamMap);
Assert.notNull(sysUserPOList);
}
项目已托管到 git 上有兴趣的可以检下来本地跑一跑,这次仅做抛砖引玉只用,如对你有帮助有启用,别忘记点赞。
https://gitee.com/LanboEx/hiber-common-query
Hibernate 离线对象构建通用查询的更多相关文章
- hibernate离线查询DetachedCriteria清除上次的查询条件
1 原例概述 别名重复问题之后,我们还需要解决的问题就是: 如何清除hibernate的上次查询条件,如果不清除,将会导致上次的查询条件和下次的查询条件合并到了一起. 上次的查询条件和本次的查询条件合 ...
- 1.1Hibernate持久化类和Hibernate持久化对象状态
一.持久化对象po类 1.po定义 PO,是Persistent Object的缩写,是持久化类.PO是由PO=POJO+hbm映射配置组成. 2.通俗理解 PO类即持久化类,其实就是一个普通的Jav ...
- hibernate 持久化对象的三个状态
Hibernate中的对象有3种状态 瞬时对象(TransientObjects).持久化对象(PersistentObjects)和离线对象(DetachedObjects也叫做脱管对象) Tran ...
- Hibernate核心对象
1.Configuration Configuration 类负责管理Hibernate的配置信息.它包括如下内容: Hibernate运行的底层信息:数据库的URL.用户名.密码.JDBC驱动类,数 ...
- Hibernate之对象的三种状态
Hibernate之Java对象的三种状态 一.简述 本博文大部分的思想和内容引子CSND上名为 FG2006 这位大神的文章,连接地址为:http://blog.csdn.net/fg2006/ar ...
- 攻城狮在路上(壹) Hibernate(六)--- 通过Hibernate操纵对象(上)
一.Hibernate缓存简介: Session接口是Hibernate向应用程序提供的操纵数据接口的最主要接口,它提供了基本的保存.更新.删除和加载Java对象的方法. Session具有一个缓存, ...
- 使用spring+hibernate+atomikos+tomcat构建分布式事务
本文通过一个demo,介绍如何使用spring+hibernate+atomikos+tomcat构建在一个事务中涉及两个数据源的web应用. demo功能:实现一个能成功提交和回滚的涉及两个数据库数 ...
- Hibernate中对象的3种状态:瞬时态、持久态、脱管态
Hibernate的对象有3种状态,分别为:瞬时态(Transient). 持久态(Persistent).脱管态(Detached).处于持久 态的对象也称为PO(Persistence Objec ...
- Hibernate实体对象三种状态
Hibernate实体对象生命周期: 1. 自由状态(Transient,临时状态,瞬态) 在内存中自由存在,与数据库无关,未被Hibernate的Session管理 2. 持久状态(Persiste ...
随机推荐
- svn图标的含义
http://www.cnblogs.com/genhaosan/articles/5129791.html
- selenium设置chrome和phantomjs的请求头信息
selenium设置chrome和phantomjs的请求头信息 出于反爬虫也好-跳转到手机端页面也好都需要设置请求头,那么如何进行呢? 目录 一:selenium设置phantomjs请求头: ...
- Codeforces 1105C Ayoub and Lost Array (计数DP)
<题目链接> 题目大意: 有一个长度为 n 的数列的未知数列,数列的每一个数的值都在区间 [l,r] 的范围内.现在问你能够构成多少个这样的数组,使得数组内的所有数的和能够被 3 整除. ...
- HDU 2586 How far away ?(经典)(RMQ + 在线ST+ Tarjan离线) 【LCA】
<题目链接> 题目大意:给你一棵带有边权的树,然后进行q次查询,每次查询输出指定两个节点之间的距离. 解题分析:本题有多重解决方法,首先,可用最短路轻易求解.若只用LCA解决本题,也有三种 ...
- JavaScript中值类型与引用类型
JavaScript中的变量类型有哪些? 值类型:字符串(string).数值(number).布尔值(boolean).null.undefined 引用类型:对象(Object).数组(Array ...
- C# JSON 转换
// using System.Web.Script.Serialization; var jser = new JavaScriptSerializer(); var json = jser.Ser ...
- Git 基本命令行操作
一.本地仓库操作 1.初始化 git init 2.添加版本控制文件 git add README.md3.本地提交 git commit -m “1st commit”4.配置远程 ...
- Shell脚本笔记(一)一些零碎的基础知识
一些零碎的基础知识 一.认识Shell脚本 一)相关概念 Shell是一种命令解释器,作用是按次序执行(遇到子脚本,先执行子脚本的命令)用户输入的命令和程序. Shell脚本语言是弱类型语言,与其他脚 ...
- 安装vmware 已经配置Centos7
一:安装vmware VMware14 安装CentOS7及其配置;CentOS7配置网桥,做远程连接; 1.VMware14安装 进入百度链接,按照图形安装就好了.https://ji ...
- 编程菜鸟的日记-初学尝试编程-编写函数实现strcat
#include <iostream>using namespace std;char *mystrcat(const char *str1,const char *str2){ char ...