通用mapper的使用+简单的条件+分页查询

通用mapper的使用+简单的条件+分页查询,第1张

启动类上

简单的条件分页查询要的要导的坐标

yal中写mysql的账号,密码,url。这个就不贴图了。

main层

Service层

因为导入了通用mapper所有可以用。

查询的可以替换为List userList =userMapper.select(user)

mapper层 

不调用其他类中的方法也可以。比如@select等

mapper层调用的类

这个方法一定要是public否则会报错

如何开发自己的通用Mapper

要求

自己定义的通用Mapper必须包含泛型,例如MysqlMapper<T>。

自定义的通用Mapper接口中的方法需要有合适的注解。具体可以参考Mapper

需要继承MapperTemplate来实现具体的 *** 作方法。

通用Mapper中的Provider一类的注解只能使用相同的type类型(这个类型就是第三个要实现的类。)。实际上method也都写的一样。

HsqldbMapper实例

第一步,创建HsqldbMapper<T>

public interface HsqldbMapper<T> {

}

这个接口就是我们定义的通用Mapper,具体的接口方法在**第三步**写。其他的Mapper可以继承这个HsqldbMapper<T>。

第二步,创建HsqldbProvider

public class HsqldbProvider extends MapperTemplate {

    //继承父类的方法

    public HsqldbProvider(Class<?> mapperClass, MapperHelper mapperHelper) {

        super(mapperClass, mapperHelper)

    }

}

这个类是实际处理 *** 作的类,需要继承MapperTemplate,具体代码在**第四步**写。

第三步,在HsqldbMapper<T>中添加通用方法

这里以一个分页查询作为例子。 public interface HsqldbMapper { /** * 单表分页查询 * * @param object * @param offset * @param limit * @return */ @SelectProvider(type=HsqldbProvider.class,method = "dynamicSQL") List selectPage(@Param("entity") T object, @Param("offset") int offset, @Param("limit") int limit)}

返回结果为List,入参分别为查询条件和分页参数。在Mapper的接口方法中,当有多个入参的时候建议增加@Param注解,否则就得用param1,param2...来引用参数。

同时必须在方法上添加注解。查询使用SelectProvider,插入使用@InsertProvider,更新使用UpdateProvider,删除使用DeleteProvider。不同的Provider就相当于xml中不同的节点,如<select>,<insert>,<update>,<delete>。

因为这里是查询,所以要设置为SelectProvider,这4个Provider中的参数都一样,只有type和method。

type必须设置为实际执行方法的HasqldbProvider.class,method必须设置为"dynamicSQL"。

通用Mapper处理的时候会根据type反射HasqldbProvider查找方法,而Mybatis的处理机制要求method必须是type类中只有一个入参,且返回值为String的方法。"dynamicSQL"方法定义在MapperTemplate中,该方法如下:

public String dynamicSQL(Object record) {

    return "dynamicSQL"

}

这个方法只是为了满足Mybatis的要求,没有任何实际的作用。

第四步,在HsqldbProvider中实现真正处理Sql的方法

在这里有一点要求,那就是HsqldbProvider处理HsqldbMapper<T>中的方法时,方法名必须一样,因为这里需要通过反射来获取对应的方法,方法名一致一方面是为了减少开发人员的配置,另一方面和接口对应看起来更清晰。

除了方法名必须一样外,入参必须是MappedStatement ms,除此之外返回值可以是void或者SqlNode之一。

这里先讲一下通用Mapper的实现原理。通用Mapper目前是通过拦截器在通用方法第一次执行的时候去修改MappedStatement对象的SqlSource属性。而且只会执行一次,以后就和正常的方法没有任何区别。

使用Provider注解的这个Mapper方法,Mybatis本身会处理成ProviderSqlSource(一个SqlSource的实现类),由于之前的配置,这个ProviderSqlSource种的SQL是上面代码中返回的"dynamicSQL"。这个SQL没有任何作用,如果不做任何修改,执行这个代码肯定会出错。所以在拦截器中拦截符合要求的接口方法,遇到ProviderSqlSource就通过反射调用如HsqldbProvider中的具体代码去修改原有的SqlSource。

最简单的处理Mybatis SQL的方法是什么?就是创建SqlNode,使用DynamicSqlSource,这种情况下我们不需要处理入参,不需要处理代码中的各种类型的参数映射。比执行SQL的方式容易很多。

有关这部分的内容建议查看通用Mapper的源码和Mybatis源码了解,如果不了解在这儿说多了反而会乱。

下面在HsqldbProvider中添加public SqlNode selectPage(MappedStatement ms)方法:

/**

 * 分页查询

 * @param ms

 * @return

 */

public SqlNode selectPage(MappedStatement ms) {

    Class<?> entityClass = getSelectReturnType(ms)

    //修改返回值类型为实体类型

    setResultType(ms, entityClass)

    List<SqlNode> sqlNodes = new ArrayList<SqlNode>()

    //静态的sql部分:select column ... from table

    sqlNodes.add(new StaticTextSqlNode("SELECT "

            + EntityHelper.getSelectColumns(entityClass)

            + " FROM "

            + tableName(entityClass)))

    //获取全部列

    List<EntityHelper.EntityColumn> columnList = EntityHelper.getColumns(entityClass)

    List<SqlNode> ifNodes = new ArrayList<SqlNode>()

    boolean first = true

    //对所有列循环,生成<if test="property!=null">[AND] column = #{property}</if>

    for (EntityHelper.EntityColumn column : columnList) {

        StaticTextSqlNode columnNode

                = new StaticTextSqlNode((first ? "" : " AND ") + column.getColumn()

                         + " = #{entity." + column.getProperty() + "} ")

        if (column.getJavaType().equals(String.class)) {

            ifNodes.add(new IfSqlNode(columnNode, "entity."+column.getProperty()

                         + " != null and " + "entity."+column.getProperty() + " != '' "))

        } else {

            ifNodes.add(new IfSqlNode(columnNode, "entity."+column.getProperty() + " != null "))

        }

        first = false

    }

    //将if添加到<where>

    sqlNodes.add(new WhereSqlNode(ms.getConfiguration(), new MixedSqlNode(ifNodes)))

    //处理分页

    sqlNodes.add(new IfSqlNode(new StaticTextSqlNode(" LIMIT #{limit}"),"offset==0"))

    sqlNodes.add(new IfSqlNode(new StaticTextSqlNode(" LIMIT #{limit} OFFSET #{offset} "),"offset>0"))

    return new MixedSqlNode(sqlNodes)

}

注:对这段代码感觉吃力的,可以对比本页最下面**结构**部分XML形式的查看。

首先这段代码要实现的功能是这样,根据传入的实体类参数中不等于null(字符串也不等于'')的属性作为查询条件进行查询,根据分页参数进行分页。

先看这两行代码:

//获取实体类型

Class<?> entityClass = getSelectReturnType(ms)

//修改返回值类型为实体类型

setResultType(ms, entityClass)

首先获取了实体类型,然后通过setResultType将返回值类型改为entityClass,就相当于resultType=entityClass。

这里为什么要修改呢?因为默认返回值是T,Java并不会自动处理成我们的实体类,默认情况下是Object,对于所有的查询来说,我们都需要手动设置返回值类型。

对于insert,update,delete来说,这些 *** 作的返回值都是int,所以不需要修改返回结果类型。

之后从List<SqlNode>sqlNodes = new ArrayList<SqlNode>()代码开始拼写SQL,首先是SELECT查询头,在EntityHelper.getSelectColumns(entityClass)中还处理了别名的情况。

然后获取所有的列,对列循环创建<if entity.property!=null>column = #{entity.property}</if>节点。最后把这些if节点组成的List放到一个<where>节点中。

这一段使用属性时用的是 entity. + 属性名,entity来自哪儿?来自我们前面接口定义处的Param("entity")注解,后面的两个分页参数也是。如果你用过Mybatis,相信你能明白。

之后在<where>节点后添加分页参数,当offset==0时和offset>0时的分页代码不同。

最后封装成一个MixedSqlNode返回。

返回后通用Mapper是怎么处理的,这里贴下源码:

SqlNode sqlNode = (SqlNode) method.invoke(this, ms)

DynamicSqlSource dynamicSqlSource = new DynamicSqlSource(ms.getConfiguration(), sqlNode)

setSqlSource(ms, dynamicSqlSource)

返回SqlNode后创建了DynamicSqlSource,然后修改了ms原来的SqlSource。

第五步,配置通用Mapper接口到拦截器插件中

<plugins>

    <plugin interceptor="com.github.abel533.mapper.MapperInterceptor">

        <!--================================================-->

        <!--可配置参数说明(一般无需修改)-->

        <!--================================================-->

        <!--UUID生成策略-->

        <!--配置UUID生成策略需要使用OGNL表达式-->

        <!--默认值32位长度:@java.util.UUID@randomUUID().toString().replace("-", "")-->

        <!--<property name="UUID" value="@java.util.UUID@randomUUID().toString()"/>-->

        <!--主键自增回写方法,默认值MYSQL,详细说明请看文档-->

        <property name="IDENTITY" value="HSQLDB"/>

  <!--序列的获取规则,使用{num}格式化参数,默认值为{0}.nextval,针对Oracle-->

  <!--可选参数一共3个,对应0,1,2,分别为SequenceName,ColumnName,PropertyName-->

        <property name="seqFormat" value="{0}.nextval"/>

        <!--主键自增回写方法执行顺序,默认AFTER,可选值为(BEFORE|AFTER)-->

        <!--<property name="ORDER" value="AFTER"/>-->

  <!--支持Map类型的实体类,自动将大写下划线的Key转换为驼峰式-->

  <!--这个处理使得通用Mapper可以支持Map类型的实体(实体中的字段必须按常规方式定义,否则无法反射获得列)-->

  <property name="cameHumpMap" value="true"/>

        <!--通用Mapper接口,多个用逗号隔开-->

        <property name="mappers" value="com.github.abel533.mapper.Mapper,com.github.abel533.hsqldb.HsqldbMapper"/>

    </plugin>

</plugins>

这里主要是**mappers**参数:

<property name="mappers" value="com.github.abel533.mapper.Mapper,com.github.abel533.hsqldb.HsqldbMapper"/>

多个通用Mapper可以用逗号隔开。

为什么mapper要继承mysqlmapper

SqlParameter是以键值对的形式传参数的,第一个参数是键,第二个是值,在systemDatetime在签名加个@,应该写成@systemDatetime。而且你前面cmd.CommanText = "select @systemDatetime = SYSDATE()"获得系统时间好像也不对,你先试试


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/8522904.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-17
下一篇 2023-04-17

发表评论

登录后才能评论

评论列表(0条)

保存