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的概念来使用没有一点问题。

二.功能说明

  1. 注释标签配置数据库映射关系,简单,直观,通过配置的注释标签可以自动建表
  2. 数据处理直接连接到JDBC保存速度快捷.
  3. 参数控制映射对象是否加载,更灵活,快速.查询细粒度
  4. 多种查询处理方式.可以使用hibernate的Criteria方式,sql查询方式和Ibatis的sql映射配置方式.还提供了简单的ssql表达式查询方式
  5. 类似hibernate的数据库适配器,Dialect支持主流数据库.采用Criteria方式,目前对数据库Postgresq 8.3x以上版本和mysql支持比较好.
  6. 提供事务支持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表示数组序号
标签属性 参数说明
mapping 映射关系
name 自己表的字段
targetName 对应的外部表字段
targetEntity 对应的实体类
term 条件 使用 ssql表达式
where 映射条件,这里为表达式,如果成立,才载入映射
orderBy 排序 ,ssql表达式
delete 关联删除
update 关联更新
chain 多层关联,查询
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 &lt;= 3; i++)
            {
                VoteTopic tbx = new VoteTopic();
                tbx.setTopicText("放入测试" + i);
                List&lt;VoteItem&gt; voteItemList = new LinkedList&lt;VoteItem&gt;();
                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&lt;DataMap&gt;  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)) &gt;= 0)
            {
                if (nbRead &gt;= 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&gt;=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 不等于 <=
BETWEENBETWEEN
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);