sober数据库框架
一.简介
sober的功能主要是DAO和orm,hibernate它引导了orm这块的进步,搞java数据库的都不太可能没听说过它.我也使用了很长时间的hibernate但hibernate有不少让我感觉不满意的地方,很多人说hibernate慢,其实不然hibernate能够做出很快的数据库应用来,但它的设计上太为了oo而oo,制造了很多的陷阱,让不熟悉的人一用就头痛.特别是在映射和延时加载上,如果你完全的按照oo来设计你的数据库,不是慢就是问题.而且最不舒服的地方hibernate的缓存,用了和没用一样,hibernate值在缓存中保存了数据记录的ID并没有实体,下一次查询再一个一个的查询出来,痛苦的事情(不知道现在改进了没,我已经没有使用了).
caucho公司,就是resin的开发公司,里边有个Amber项目,一个很有研究价值的orm,sober就是模仿了它的映射配置方式,ibatis 是 apache的orm从配置和速度上来说不错,但很多人对它的分页愤愤不平.sober提供了它的SQL映射配置方式查询.在看过这些orm后最后我决定自己写一个orm就是sober.让简单的数据,保存,编辑,增加如同hibernate一样方便,配置如同amber一样人性,复杂的SQL使用ibatis的映射方式完成.严格的来说sober只是一个JDBC扩展。它能够让你简单方便的操作数据库
和hibernate的不同点说明.很多人感觉hibernate不好用的问题在于对hibernate不了解。 很多人想当然的使用JDBC的概念来使用hibernate.这是一个错误的开始。因为hibernate的宣传就是让你不必了解细节直接操作数据,但这样就会出现不少细节上的问题,有种用把老虎钳当扳手使用的感觉,有种脱离过去学习的知识感。 因为hibernate并不是一个jdbc的简单扩展,他包括了映射管理和缓存管理,HQL转换,内部还包括了复杂统计和sql适配器。 如果你不了解hibernate又使用了不少hibernate的高级特征还在做复杂的关联操作,很可能就掉入无穷的陷阱中。在hibernate 后期版本中感觉缓存功能改进了不少。
因为hibernate的关联查询比较弱,也不太适合中国的国情,因为中国开发经常要修改阿修改啊的,如果关联多了hibernate越改越慢,改起来痛苦。
这样所以有了写Sober的想法,Sober是一个完全的JDBC的扩展,内部没有复杂的缓存,数据统计这些。对JDBC直接的操作。让你的操作和JDBC没区别,同时又既有了hibernate的简便性。就用JDBC的概念来使用没有一点问题。
二.功能说明
- 注释标签配置数据库映射关系,简单,直观,通过配置的注释标签可以自动建表
- 数据处理直接连接到JDBC保存速度快捷.
- 参数控制映射对象是否加载,更灵活,快速.查询细粒度
- 多种查询处理方式.可以使用hibernate的Criteria方式,sql查询方式和Ibatis的sql映射配置方式.还提供了简单的ssql表达式查询方式
- 类似hibernate的数据库适配器,Dialect支持主流数据库.采用Criteria方式,目前对数据库Postgresq 8.3x以上版本和mysql支持比较好.
- 提供事务支持JDBCTransaction和JTATransaction.让你能够应用在复杂的高安全环境
三.配置使用说明
下边我们使用sioc配置方式, 当然你也可以使用代码写程序实现配置.
首先你需要配置一个数据源,本构架提供了了一个数据库连接池,速度和稳定性都很优秀了,完全没必要在事情其他连接池。当然如果你喜欢其他连接池也一样,比如c3p0
sioc配置
<bean id="jspxDataSource" class="com.jspx.datasource.JspxDataSource" destroy="close" singleton="true">
<string name="driverClass">${driverClassName}</string>
<string name="jdbcUrl"><![CDATA[${jdbcUrl}]]></string>
<string name="user">${username}</string>
<string name="password"><![CDATA[${password}]]></string>
<int name="maxPoolSize">${maxPoolSize}</int>
<int name="readWrite">0</int>
</bean>
顺便演示一下在sioc中,配置c3p0数据连接池例子
下边例子演示了c3p0的连接池配置,换一下类命就可以了很方便。 (使用的时候一个就可以了)
<bean id="jspxDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy="close" singleton="true">
<string name="driverClass">${driverClassName}</string>
<string name="jdbcUrl">${jdbcUrl}</string>
<string name="user">${username}</string>
<string name="password">${password}</string>
<int name="maxPoolSize">${maxPoolSize}</int>
</bean>
下边是sober的环境配置,com.jspx.sober.config.SoberMappingBean是配置bean,使用单列运行
<!--Sober begin -->
<bean id="jspxSoberFactory" class="com.jspx.sober.config.SoberMappingBean" singleton="true">
<!--数据库适配器,更具数据库生成不同的SQL-->
<string name="databaseName">${dialect}</string>
<ref name="dataSource">jspxDataSource</ref>
<boolean name="useCache">${useCache}</boolean>
<ref name="dataSource">jspxDataSource</ref>
<!--是否显示SQL ${show_sql}-->
<string name="showsql">${show_sql}</string>
<!--载入sql map-->
<array name="mappingResources" class="string">
<value>*.sqlmap.xml</value>
</array>
</bean>
<!--Sober end -->
<!--这里配置一个简单的DAO-->
<bean id="com.jspx.sober.GenericDAO" class="com.jspx.sober.GenericDAO" singleton="true">
<ref name="soberFactory">jspxSoberFactory</ref>
</bean>
四. 注释标签映射配置说明
标签属性 |
类型 |
参数说明 |
caption | String | 字段文字说明 |
notNull | boolean | 字段是否可以为空 |
option | String | 选择范围,例如:"小;中;大"或者 "1:小;2:中;3:大" |
dataType | String | 验证表达式,配置和TXWeb里边的验证配置是一样的,这里配置好后可以自动生成web页面的配置 |
defaultValue | String | 默认值,映射到数据库,如果是数字等,例如:"1.14" |
length | int | 字段长度,映射到数据库,日期类型不需要配置 |
input | String | 输入框类型,对应html表单 的type 属性,例如:text,number,date |
hidden | boolean | 在导出的时候是否隐藏,例如密码或者一些辅助字段就可以设置为true |
标签属性 |
类型 |
参数说明 |
auto | boolean | 是否自动生成ID |
type | String | ID号的生成方式,详细见下表 |
length | int | ID号长度,推荐最好小于18, |
max | long | id的最大值,到达后会循环到最小值,默认Integer.MAX_VALUE |
min | int | id的最小值,根据next 递增 |
next | int | id递增量 id=id+nex |
dateStart | boolean | 使用sober生成id时,是否使用日期开头,例如yyyyMMddhhss + 序列 |
标签属性 |
java类型 |
参数说明 |
uid | String | 默认生成格式如 b2a53eef-3a29-44ff-bca5-72abc9568785 |
uid | long | UUID getMostSignificantBits 生成 |
serial | long 或 int | 数据库自动增加,auto必须为false |
seq | String或long或int | 交由sober配合上边的相关参数生成 |
标签属性 |
参数说明 |
entity | 是一个数组,包括了你在本sql中会用到的实体bean |
sql | 统计时用到的sql |
例如:统计关联统计Remark.class 实体中,matterId 字段为,本实体中id的记录是多少条
sql = "SELECT COUNT(*) FROM ${entity1} WHERE matterId=${id}", entity ={Remark.class}
entity1:是实体1,你要用到是实体,entity是数组,1,2,3表示数组序号
- @Nexus标签,表示映射关系,mapping关系可以在MappingType得到定义
标签属性 |
参数说明 |
mapping | 映射关系 |
name | 自己表的字段 |
targetName | 对应的外部表字段 |
targetEntity | 对应的实体类 |
term | 条件 使用 ssql表达式 |
where | 映射条件,这里为表达式,如果成立,才载入映射 |
orderBy | 排序 ,ssql表达式 |
delete | 关联删除 |
update | 关联更新 |
chain | 多层关联,查询 |
- @Nexus标签,mapping备选定义在com.jspx.boot.sign.MappingType.java
mapping属性 |
参数说明 |
ManyToOne | 多对一 |
OneToOne | 一对一 |
OneToMany | 一对多 |
五.配置一个简单的bean演示
//运行的时候数据中会自动创建testBean这个表名称,变量名称将作为表的字段名称
@Table(name = "testBean",caption="sober例子1")
public class TestBean implements Serializable
{
public TestBean()
{
}
@Id(auto = true) //ID使用自动创建
@Column(caption = "ID", length = 50, notNull = true)
private String id = "";
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
//caption显示,dataType=isBetween(10,100) 表示只能是10,100两个数值范围
@Column(caption = "数量",dataType=isBetween(10,100)")
private int mum = 0;
public int getMum()
{
return mum;
}
public void setMum(int mum)
{
this.mum = mum;
}
@Column(caption = "创建日期")
private Date createDate = new Date();
public Date getCreateDate()
{
return createDate;
}
public void setCreateDate(Date createDate)
{
this.createDate = createDate;
}
//数据库将创建能够保存250字符的字段, isLengthBetween(10,100)表示长度范围在10-100之间
@Column(caption = "内容", length = 250,valid="isLengthBetween(10,100)",notNull=true)
private String topicText = null;
public String getTopicText()
{
return topicText;
}
public void setTopicText(String topicText)
{
this.topicText = topicText;
}
}
如果在web方式下,配置好拦截器,会自动初始化,如果不是在web服务器里边运行,作为应用运行的初始化,我们使用下边语句来初始化应用.
public static void main(String arge[]) throws Exception
{
//使用下边语句来初始化应用
JspxNetApplication.autoRun();
//通过环境得到Sioc 的bean工厂
BeanFactory beanFactory = EnvFactory.getBeanFactory();
SoberSupport genericDAO = (SoberSupport) beanFactory.getBean(GenericDAO.class.getName());
TestBean testBean = new TestBean();
testBean.setMum(11);//如果是10-100就可以保存成功,否则将会报错,不允许保存
testBean.setTopicText("topicText"); //lengthBetween[10-20],文字长度在10-20范围,否则报错
try
{
//sober当开启验证方式,sober会更具TestBean的校验来验证数据,不成功就会异常你可以通过ValidException得到详细信息
genericDAO.save(testBean, false); //保存数据,这里返回被操作的记录条数1
//如果你的ID是自动生成的,那边保存成功后自动生成的ID已经被放入testBean中了.
//如果要删除 genericDAO.delete(testBean);
} catch (ValidException e) {
Map mp = e.getValidMap();
for (Object key : mp.keySet())
{
//这里将会打印验证错误的信息
System.out.println("--------------" + key + "=" + mp.get(key));
}
}
}
六.映射关系的例子
一个投票的设计,我们来配置写两个bean分别映射到数据库.并且里边配置一对多和多对一的方式.演示例子使用投票的关系,一个投票主题表VoteTopic 和投票选项Vote 我们使用两个表来分别保存数据。
com.jspx.example。VoteTopic.java
package com.jspx.example;
import com.jspx.boot.sign.MappingType;
import com.jspx.sober.annotation.*;
import com.jspx.sober.table.OperateTable;
import com.jspx.utils.DateUtil;
import com.jspx.utils.StringUtil;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@Data
@Table(name = "test_votetopic", caption = "投票")
public class VoteTopic extends OperateTable
{
@Id
@Column(caption="ID",notNull = true)
private long id = 0;
@Column(caption = "组ID", length = 50, notNull = false, defaultValue = "")
private String groupId = StringUtil.empty;
@Column(caption = "选项", length = 250, notNull = true, defaultValue = "")
private String topicText = StringUtil.empty;
@Column(caption = "类型", option = "0:单选;1:多选", notNull = true, defaultValue = "0")
private int voteType = 0;
//默认为空表示任意,更具后台设置判断多个使用 ;分割
@Column(caption = "允许投票的角色", length = 250, notNull = true, defaultValue = "")
private String roleIds = StringUtil.empty;
@Column(caption = "表示图", option = "0:条型;1:柱状;2:饼型;4:线型", notNull = true, defaultValue = "0")
private int shape = 0;
@Column(caption = "重复投票", option = "0:否;1:是", notNull = true, defaultValue = "0")
private int repeat = 0;
@Column(caption = "一次投票次数" ,length = 4, notNull = true, defaultValue = "0")
private int times = 0;
@Column(caption = "投票结束时间", notNull = true)
private Date endDate = DateUtil.addYear(2);
@Column(caption = "排序时间", notNull = true)
private Date sortDate = new Date();
@Column(caption = "投票才能看结果",option = "0:否;1:是",length = 2,notNull = true, defaultValue = "0")
private int pollLook = 0;
@Column(caption = "排序", notNull = true, defaultValue = "0")
private int sortType = 0;
@Column(caption = "最后操作时间", notNull = true)
private Date lastDate = new Date();
@Nexus(mapping = MappingType.OneToMany, name = "id", targetName = "topicId", orderBy = "sortType:A", targetEntity = VoteItem.class,delete=true,update=true)
private List<VoteItem> voteItemList = new LinkedList<VoteItem>();
@Column(caption = "命名空间", length = 50, notNull = true)
public String namespace = StringUtil.empty;
public void setPollLook(int pollLook)
{
this.pollLook = pollLook;
}
public int getSumPoint()
{
int result=0;
for (VoteItem vv : getVoteItemList())
{
result = result + vv.getVotePoint();
}
return result;
}
}
*Vote 投票选项表 *
com.jspx.example.VoteItem.java
package com.jspx.example;
import com.jspx.boot.sign.MappingType;
import com.jspx.sober.annotation.*;
import com.jspx.sober.table.OperateTable;
import com.jspx.utils.NumberUtil;
import com.jspx.utils.StringUtil;
@Data
@Table(name = "test_voteitem", caption = "投票选项")
public class VoteItem extends OperateTable
{
@Id(auto = true,type= IDType.serial)
@Column(caption="ID",notNull = true)
private long id;
@Column(caption = "排序", notNull = true, defaultValue = "0")
private int sortType = 0;
@Column(caption = "投票主题的ID", length = 50, notNull = true)
private long topicId = 0;
@Nexus(mapping = MappingType.ManyToOne, name = "topicId", targetName = "id", targetEntity = VoteTopic.class)
private VoteTopic voteTopic;
@Column(caption = "投票选项说明", length = 100, notNull = true)
private String title = StringUtil.empty;
@Column(caption = "图片投票", length = 100, notNull = false)
private String images = StringUtil.empty;
@Column(caption = "票数", notNull = true, defaultValue = "0")
private int votePoint = 0;
public String getColor()
{
switch (sortType)
{
case 1:
return "00EEAA";
case 2:
return "0099CC";
case 3:
return "990000";
case 4:
return "0033F0";
case 5:
return "FFFF00";
case 6:
return "FF99CC";
case 8:
return "0F9900";
case 9:
return "003300";
case 10:
return "00FFCC";
case 11:
return "00EEEE";
case 12:
return "00CC00";
case 13:
return "aa33B0";
}
return "F0" + Integer.toHexString(sortType);
}
/**
* scale
* 得到投票比例
*
* @return double
*/
public Float getScale(int sum)
{
if (sum<=0) return (float) 0;
return (NumberUtil.mul(NumberUtil.div(votePoint, sum).doubleValue(), 100)).floatValue();
}
/**
* 得到投票比例
*
* @return double
*/
public String getScaleTwo(int sum)
{
return NumberUtil.NumberFormat(getScale(sum) * 2, "######");
}
public String getScaleString(int sum)
{
return NumberUtil.NumberFormat(getScale(sum), "######");
}
}
映射关系说明
@Nexus(mapping = MappingType.ManyToOne, name = "topicId", targetName = "id", targetEntity = VoteTopic.class)
private VoteTopic voteTopic;
这是一个多对一的关系 topicId 字段对应VoteTopic.class的id字段,dao查询时,载入参数设置为true,就会关联查询出来
演示一下事务和JTA事务功能
public static void main(String arge[]) throws Exception
{
//下边语句来初始化应用
JspxNetApplication.autoRun();
//通过环境得到Sioc 的bean工厂
BeanFactory beanFactory = EnvFactory.getBeanFactory();
SoberSupport genericDAO = (SoberSupport) beanFactory.getBean(GenericDAO.class.getName());
for (int i = 0; i <= 3; i++)
{
VoteTopic tbx = new VoteTopic();
tbx.setTopicText("放入测试" + i);
List<VoteItem> voteItemList = new LinkedList<VoteItem>();
VoteItem v = new VoteItem();
v.setTitle("子A" + i);
voteItemList.add(v);
v = new VoteItem();
v.setTitle("子B" + i);
voteItemList.add(v);
tbx.setVoteItemList(voteItemList);
genericDAO.save(tbx, true); //这里的true,表示是否保存映射对象
}
//删除对象,例如我们要删除VoteTopic的id为9的记录
VoteTopic v = new VoteTopic();
v.setId(9);
int delNum = genericDAO.delete(v, true);
//这里true表示是否删除映射对象
/*
也可以这样不删除映射对象
genericDAO.delete(VoteTopic.class, 29)
更新
VoteTopic voteTopic = (VoteTopic) genericDAO.get(VoteTopic.class, "VoteTopic的ID", false); //这里的false表示不载入映射对象
voteTopic.setTopicText("保存编辑新的内容");
//更新对象
genericDAO.update(voteTopic);
*/
}
看看是不是很象hibernate,使用他们完成基本的数据操作很舒服吧.而且粒度更细一些
update可以只更新指定的字段,例如: genericDAO.update(voteTopic,new String[]{'title'});
七.模版sql映射查询方式
下边我们来看看 ibatis方式的映射sql查询?看上边配置文件载入使用的是*..sqlmap.xml,所以在会在启动的时候自动载入sql配置文件 votetopic.sqlmap.xml
使用的是scriptmark模版语言(当应用在存在复杂的多表时推荐使用这种查询方式,因为这种查询方式比较灵活,而且优化的sql能够很好的提高性能)
votetopic.sqlmap.xml,语法上和ibatis这里有些不同,引号等特殊字符,在 <![CDATA[ 有特殊字符的sql ]]>中可以直接用,可以使用复杂的javascript语法。
<?xml version="1.0" encoding="UTF-8"?>
<!--namespace可以是类名,软件名称,更具你的应用来给sql分一个类别-->
<sqlMap namespace="example">
<querys>
<query id="getVoteTopic" class="com.jspx.example.VoteTopic">
<![CDATA[select * from ${table_name} where id=${id}]]>
</query>
<query id="getVoteTopicMap" class="map">
SELECT * FROM ${table_name} WHERE voteType=${voteType} limite ${currentPage},${totalCount}
</query>
<query id="getVoteTopicObject" class="jspx.apply.table.VoteTopic">
SELECT * FROM ${table_name} WHERE voteType=${voteType}
</query>
</querys>
<updates>
<update id="updateVoteTopic">
UPDATE from ${table_name} SET voteType=${voteType} where id=${id} ;
</update>
</updates>
<executes>
<execute id="deleteVoteTopic">
delete from ${table_name} where id=${id}
</execute>
</executes>
</sqlMap>
查询列表 模版中放入了默认的几个变量
databaseName:数据库名,方便你实现跨数据库的映射配置
currentPage:当前页
totalCount:每页显示行数
loadChild:是否导入映射对象(优化查询)
rollRows:是否滚动到行
//创建SQL映射查询器
SqlMapClient sqlMap = genericDAO.buildSqlMap();
//第1页,每页2行,是否载入映射对象,是否使用滚动到行(如果你在配置的 SQL中不需要滚动到行了,就设置为false),
//例如: SELECT * FROM ${table_name} WHERE voteType=${voteType} limite ${currentPage},${totalCount}
//这时候就不需要在滚动到行了.
Map<String, Object> valueMap = new HashMap<String, Object>();
TableModels soberTable = getSoberTable(VoteTopic".class);
//动态得到数据库表名
valueMap.put("table_name", soberTable.getTableName());
valueMap.put("voteType", city);
//example表示命名空间
List list = sqlMapClient.queryForList("example", getVoteTopicMap, valueMap, 1, 2, true, true);
for (DataMap valueMap : list)
{
for (Object keys : valueMap.keySet())
{
//字段=值
System.out.println(keys + "=" + valueMap.get(keys));
}
}
//查询返回单个对象
sqlMap.getUniqueResult(...);
//更新
sqlMap.update(...);
//执行SQL
sqlMap.execute(...);
//DataMap 是扩展的HashMap 能够方便的转换数据类型
List<DataMap> list = sqlMap.queryForList(VoteTopic.class,"getVoteTopicMap", voteTopic, 1, 2, true,true);
for (DataMap valueMap : list)
{
for (Object keys : valueMap.keySet())
{
//字段=值
System.out.println(keys + "=" + valueMap.get(keys));
}
}
这部分在使用ide开发工具,会自动提示你相关参数
最后还提供了hibernate的Criteria扩展方法.用法和hibernate的差不多(推荐非关联查询使用方法)
Criteria criteria = genericDAO.createCriteria(VoteTopic.class);
//设置第1页,返回3行
criteria = criteria.setCurrentPage(1).setTotalCount(2);
criteria = criteria.addOrder(Order.desc("id"));
//查询得到列表,参数表示是否载入映射对象
List list = criteria.list(false);
//删除,参数含义:是否删除映射对象,指删除一对一,和一对多,不删除多对一
criteria.delete(false);
//查询返回单个对象
criteria.uniqueResult();
你也可直接使用sql,来完成查询,更新
查询VoteTopic中2页每页5个数据,最后的false表示不载入映射对象.(更具应用时间需要)
List list = genericDAO.queryForList(VoteTopic.class, "select * from ${table_name}", null, 2,5, false);
八.保存读取图片的例子
下边在做一个保持读取图片的例子,我们先定义一个实体bean
import com.jspx.sober.annotation.Column;
import com.jspx.sober.annotation.Id;
import com.jspx.sober.annotation.Table;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Date;
@Data
@Table(name = "testBean2",caption="保存图片")
public class TestBean2 implements Serializable
{
public TestBean2()
{
}
@Id(auto = true)
@Column(caption = "ID", notNull = true)
private int id = 0;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
@Column(caption = "数量")
private int mum = 0;
public int getMum()
{
return mum;
}
public void setMum(int mum)
{
this.mum = mum;
}
@Column(caption = "创建日期")
private Date createDate = new Date();
public Date getCreateDate()
{
return createDate;
}
public void setCreateDate(Date createDate)
{
this.createDate = createDate;
}
@Column(caption = "图片")
private InputStream image = null; //这个字段将保存图片
public InputStream getImage()
{
return image;
}
public void setImage(InputStream image)
{
this.image = image;
}
}
在这里是保存图片,和查询出来的代码
TestBean2 tb = new TestBean2();
//放入图片假设图片路径为d:/JD040.jpg ,当然也可以是别的文件
tb.setImage(new java.io.FileInputStream(new File("d:/JD040.jpg")));
//放入其他数据
tb.setMum(18);
//保持到数据库
soberTemplate.save(tb); //保存到数据库中
System.out.println(tb.getId());
//------------------------
//再查询输出来
TestBean2 tbb = (TestBean2) genericDAO.get(TestBean2.class, tb.getId());
//输出到d:/test.jpg文件,如果是在网页上可以直接输出
//保存到d:/test.jpg文件
InputStream in = tbb.getImage();
OutputStream fon = new java.io.FileOutputStream(new File("d:/test.jpg"));
byte[] data = new byte[1024];
int nbRead;
try
{
while ((nbRead = in.read(data)) >= 0)
{
if (nbRead >= 0)
{
fon.write(data, 0, nbRead);
}
}
in.close();
fon.close();
} catch (IOException e)
{
e.printStackTrace();
}
九.sqlmap注释方式查询
创建一个文件 jbbs.sqlmap.xml,jbbs标识软件名称,放在配置目录,或者在资源目录下
<?xml version="1.0" encoding="UTF-8"?>
<sqlMap namespace="jbbs">
<sql id="testInclude">
order by createDate
</sql>
<querys>
<query id="jspx.jbbs.dao.ReplyPostDAO.getTestQueryMap8" >
select g.id from jshop_goods g where g.id = '0sebmdhi1gmtx3fpgxvo0cop'
UNION all
select g.id from jshop_goods g where g.id = '0sebmdhi1gmtx3fpgxvo0cop'
</query>
<query id="jspx.jbbs.dao.ReplyPostDAO.getTestQueryMap9" class="jspx.jbbs.vo.ReplyPostDto" >
SELECT * FROM ${replyPostTable}
</query>
<query id="jspx.jbbs.dao.ReplyPostDAO.getTestQueryMap10" class="jspx.jbbs.vo.ReplyPostDto" >
SELECT * FROM ${replyPostTable} <include id="testInclude" />,id
</query>
</querys>
</sqlMap>
创建一个DAO对象,继承相应的接口,下边的接口就会自动和上边的sql关联,可以注入直接是用了
@Override
@SqlMap(namespace = Jbbs.namespace,mode = QueryModelEnumType.LIST)
public <T> List<T> getTestQueryMap8(Map<String, Object> valueMap, Class<T> type) {
//这会执行,可以补充参数,做一些查询初始化工作
valueMap.put("replyPostTable",getTableName(ReplyPost.class));
//这个返回无意义
return null;
}
@Override
@SqlMap(namespace = Jbbs.namespace,mode = QueryModelEnumType.LIST)
public <T> List<T> getTestQueryMap9(Map<String, Object> valueMap, Class<T> type) {
//这会执行,可以补充参数,做一些查询初始化工作
valueMap.put("replyPostTable",getTableName(ReplyPost.class));
//这个返回无意义
return null;
}
@Override
@SqlMap(namespace = Jbbs.namespace,mode = QueryModelEnumType.LIST)
public <T> List<T> getTestQueryMap10(Map<String, Object> valueMap, Class<T> type) {
//这会执行,可以补充参数,做一些查询初始化工作
valueMap.put("replyPostTable",getTableName(ReplyPost.class));
//这个返回无意义
return null;
}
十.sqlmap语法说明
基本语法就是scriptmark语法.区别是是用在sqlmap里边不用写#号标识
1.${ } 方式输出为原数据输出
2.#{ } 方式输出为加引号输出
3.list语法做了增强,如下
<list v="list" open="循环开始出字符" close="循环结束出字符" separator="间隔符号" empty="如果为空输出">#{v}</list>
例子:
<query id="jspx.jbbs.dao.ReplyPostDAO.getTestQueryMap3">
SELECT * FROM ${replyPostTable} r,${speakThreadTable} s WHERE r.threadId=s.id
<if where="nodeId">AND s.nodeId=#{nodeId}
</if> AND r.recycleType>=0 AND r.auditingType=1 AND r.createDate>#{toDate}
<if where="list">
and s.putName in
<list v="list" open="(" close=")" separator="," empty="(null)">#{v}</list>
</if>
</query>
十一.json过滤表达式
6.73版本开始支持
operator 操作对应表
JSON标识 |
描述 |
对应SQL |
UNKNOWN | 未知 | |
LOGIC | 逻辑表达式 | |
EQ | 等于 | = |
GT | 大于 | > |
LT | 小于 | < |
GE | 大于等于 | => |
LE | 小于等于 | <= |
NE | 不等于 | <= |
BETWEEN | 至 | BETWEEN |
IN | IN | IN |
IN_SQL | IN SQL | IN |
NOT IN | NOT IN | NOT IN |
NOTINSQL | NOT IN SQL | NOT IN |
LIKE | 包含 | LIKE |
NOT LIKE | 不包含 | NOT LIKE |
IS NOT NULL | 非空 | IS NOT NULL |
IS NULL | 为空 | IS NULL |
FIND | 搜索 | LIKE |
NOT | 非 | NOT |
例如下边这个复杂的sql 过滤条件
((id=1 AND name LIKE %name% AND id IN (0,1,2,3,4) AND id NOT IN (0,1,2,3,4) AND id>4 AND id<=0 AND NOT(id IN (100,101,102,103,104) ) AND name IS NULL AND old IS NOT NULL AND field1 LIKE '%value1%' AND field2 LIKE '%value2%' ) AND (id=10 OR id=20))
{
"logic":"AND",
"bracket":true,
"filters":[{
"logic":"AND",
"bracket":true,
"filters":[{
"operator":"EQ",
"field":"id",
"value":1
},{
"field":"name",
"operator":"LIKE",
"value":"%name%"
},{
"field":"id",
"operator":"IN",
"value":[0,1,2,3,4]
},{
"field":"id",
"operator":"NOT IN",
"value":[0,1,2,3,4]
},{
"operator":"GT",
"field":"id",
"value":4
},{
"operator":"LE",
"field":"id",
"value":0
},{
"field":{
"field":"id",
"operator":"IN",
"value":[100,101,102,103,104]
},
"operator":"NOT"
},{
"field":"name",
"operator":"IS NULL"
},{
"field":"old",
"operator":"IS NOT NULL"
},{
"field":["field1","field2"],
"operator":"FIND",
"value":["value1","value2"]
}]
},{
"logic":"OR",
"bracket":true,
"filters":[{
"operator":"EQ",
"field":"id",
"value":10
},{
"operator":"EQ",
"field":"id",
"value":20
}]
}]
}
得到查询表达式对象
LogicalExpression filterCriterionNew = new LogicalExpression(new JSONObject(上边的json));
//得到sql
System.out.println(filterCriterionEnd);
//得到json
System.out.println(filterCriterionEnd.getJson().toString(4));
如果我们手写java代码实现
List<Criterion> filters = new LinkedList<>();
Criterion criterion1 = Expression.eq("id",1);
filters.add(criterion1);
Criterion criterion2 = Expression.like("name","%name%");
filters.add(criterion2);
int[] indexes = new int[5];
for (int i = 0; i < 5; i++) {
indexes[i] = i;
}
Criterion criterion3 = Expression.in("id", indexes);
filters.add(criterion3);
Criterion criterion4 = Expression.notIn("id", indexes);
filters.add(criterion4);
Criterion criterion5 = Expression.gt("id", 4);
filters.add(criterion5);
Criterion criterion6 = Expression.lt("id", 0);
filters.add(criterion6);
int[] indexes2 = new int[5];
for (int i = 0; i < 5; i++) {
indexes2[i] = i+100;
}
//不推荐使用 Expression.not 方式,可以使用 Expression.notIn
Criterion criterion7 = Expression.not(Expression.in("id", indexes2));
filters.add(criterion7);
Criterion criterion8 = Expression.isNull("name");
filters.add(criterion8);
Criterion criterion9 = Expression.isNotNull("old");
filters.add(criterion9);
Criterion criterion10 = Expression.find(new String[]{"field1","field2"},new String[]{"value1","value2"});
filters.add(criterion10);
LogicalExpression filterCriterion = new LogicalExpression(filters,FilterLogicEnumType.AND);
System.out.println(filterCriterion.getJson().toString(4));
System.out.println(filterCriterion);
List<Criterion> filters2 = new LinkedList<>();
Criterion criterionOr1 = Expression.eq("id",10);
filters2.add(criterionOr1);
Criterion criterionOr2 = Expression.eq("id",20);
filters2.add(criterionOr2);
LogicalExpression filterCriterionOr = new LogicalExpression(filters2,FilterLogicEnumType.OR);
System.out.println(filterCriterionOr.getJson().toString(4));
System.out.println(filterCriterionOr);