1. mybatis开发DAO
下面是mybatis的入门程序-使用Mybatis实现DAO模式-本案例图简单没有定义dao接口。
目的是解决原生jdbc操作数据库存在的频繁连接硬编码问题。
mybatis是ORM持久层框架,就是和数据库打交道的。
此入门程序的优点:
提供数据源(数据库连接池)。
通过xml配置文件或者注解去映射sql和输入输出参数类型。
此入门程序的缺点:
尽管采用dao模式的编程,将CRUD操作集成在一起,但是每个方法依然存在大量重复的session,即存在大量的模板方法,用来创建 sqlSessionFactory 和 sqlSession 对象。
设想:
使用单例模式管理 sqlSessionFactory 对象,将部分模板方法提取出来,采用mybatis的原始方式开发dao模式,减少mem的开销。
注意,任何方式实现的mybatis的parameterType和resultType都只能指定一个!而不能指定多个!
但parameterType可以不指定!
2. mybatis入门程序详解
2.1 插入一条记录
2.1.1 DAO
zeh.mybatis.code02mybatis.dao.MybatisFirstDao
package zeh.mybatis.code02mybatis.dao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import zeh.mybatis.code00common.entity.ZehPerson;
import zeh.mybatis.code00common.entity.ZehUser;
import zeh.mybatis.code00common.po.singleton.CommonPo;
import zeh.mybatis.code00common.po.singleton.ZehPersonPo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
// 2019-3-6下午12:57:57
public class MybatisFirstDao {
// 定义 sqlmap-config.xml 文件的resource相对位置(即相对classpath的路径)
// mybatis配置文件,resource总是相对于classpath的根路径而言的
private static final String sqlMapConfigPath = "mybatis" + File.separator + "sqlmap" + File.separator + "sqlmap-config.xml";
// (1)
// mybatis入门程序调用 insert 操作:为 zeh_user 表新增一条record。
public void insertUser() throws IOException {
// 通过Resources工具类取得 sqlmap-config.xml 文件的输入流InputStream
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
// 传入inputStream输入流信息,通过SqlSessionFactoryBuilder实例化SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过sqlSessionFactory对象获取SqlSession会话对象
// 通过sqlSession操作底层Executor实现类,Executor执行器会从配置文件中配置的 mappedStatement 对象中拿到sql和输入输出参数,向数据库发送sql。
SqlSession sqlSession = sqlSessionFactory.openSession();
// (2)
CommonPo commonPo = new CommonPo();
commonPo.setId(8);
commonPo.setName("宋吉东");
commonPo.setAge(27);
// (3)
// commonPo.setSex("男");
commonPo.setHobby("唱歌、写作、编程");
// (4)
sqlSession.insert("first.insertUserFirst", commonPo);
// (5)
sqlSession.commit();
System.out.println("insert操作成功提交");
// (6)
sqlSession.close();
}
}
(1)insert into table values():注意,传入的pojo对象中包含一大堆参数值,这些参数值是否进行非空判断取决于sql怎么写,一般要在sql进行非空判断!(具体参照逆向工程)
(2)首先必须封装一个 commonPo 对象,将该类型的对象发送到数据库进行insert操作:
mybatis 会通过 xml 中对应的 statementId 中配置的 parameterType 指定的入参类型,反射入参所在类中的 getXXX() 方法,从而获取XXX属性。
如果 statementId 中未指定 parameterType ,mybatis 会自动根据入参实际类型匹配到对应的入参所在类,进而反射 getXXX()方法。
如果入参所在类没有 getXXX() 方法,则mybatis直接根据 statementId 中获取参数时指定的参数名称去反射入参所在类中的 成员属性名。
总结:入参所在类中有 getXXX(),则优先反射XXX;没有则直接反射属性。
通过反射获取到入参对象中对应的字段值,作为sql拼接的参数。
注意:在mybatis执行insert操作的时候,如果入参对象中有字段参数传入进去的值是null值,则mybatis会默认报错,无效的列类型。
原因分析:mybatis的statementId配置中默认没有对该字段指定jdbcType,因此mybatis在insert插入时会报类型转换错误(无效的列类型)。
因为mybatis对于null类型转换为了other类型,而oracle是不识别other类型的。
(3)上面注释掉了Sex: 注释掉Sex的set操作,由javaSE基础可知,sex字段是string,其默认值将是null,所以调用insert操作就会报无效的列类型:1111.
解决办法:
a.在对应的 statementId 的配置中,对该字段指定jdbcType,其值对应数据表中真实对应的数据表字段类型,一般都是VARCHAR, 这样mybatis在遇到null值就会将其转换成VARCHAR类型了。
b.在sqlmap-config.xml全局配置文件中配置settings参数:
(4)上面的第(4)点,将封装好的 commonPo 对象传递进去,注意 insert into 进去的所有字段的取值都应该包含在该对象中,如果尝试获取一个不存在的字段值,获取到的默认是null值。
通过SqlSession操作数据库。
第一个参数:映射文件mapper中statement的id,等于 namespace + “.”+ statement的id。
第二个参数:指定和映射文件中所匹配的parameterType类型的参数。
(5)上面的(5),insert操作需要执行事务(凡是修改数据的操作都有事务提交)。
(6)上面的(6)点,执行完毕后关闭sqlSession会话。
2.1.2 resource下定义Mybatis全局配置文件
注意,这个mybatis的全局配置文件,后面单独使用mybatis开发时都会用到,后面将不再单独列出。
mybatis/sqlmap/sqlmap-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 说明:sqlmap-config.xml文件是mybatis框架的全局核心配置文件,该文件路径和名称不固定,可以自定义,需要在代码中指定该相对路径解析该文件 。 -->
<!-- 和spring整合后,大多数的全局配置将被废弃,统一交给spring容器去管理。 -->
<!-- 所谓交给spring容器去管理指的是:目标bean的实例化过程和依赖注入统一由spring进行反向控制。 -->
<!-- 严格按照如下顺序配置各个节点,否则mybatis解析将报错: -->
<!-- properties(属性) -->
<!-- settings(全局配置参数) -->
<!-- typeAliases(类型别名,mybatis默认支持很多种别名来映射parameterType和resultType,别名不要和下面的类型转换器搞混了) -->
<!-- typeHandlers(类型处理器,mybatis默认支持很多种java类型和jdbc类型的转换器,一般不用配置) -->
<!-- objectFactory(对象工厂) -->
<!-- plugins(插件) -->
<!-- environments(环境集合属性对象) -->
<!-- environment(环境子属性对象) -->
<!-- transactionManager(事务管理) -->
<!-- dataSource(数据源) -->
<!-- mappers(映射器) -->
<!-- mybatis不能像hibernate一样默认当表不存在时自动创建表,如果需要这样的功能,需要自己编写sql实现 -->
<!-- properties引用数据源*.properties文件:将数据源配置在db的属性文件中,在 sqlmap-config 中进行加载,与Spring整个后将由Spring容器进行管理。 -->
<!-- myeclipse中可以将任何的目录都进行buildpath,buildpath之后这些目录就是classpath指定的目录,和classpath是一样的级别 -->
<!-- sqlmap-config.xml中配置的是mybatis框架的全局配置文件 -->
<!-- mapper.xml中需要使用的一些配置都首先在sqlmap-config.xml中进行指定,然后通过该文件引用mapper文件 -->
<!-- 加载属性文件,mybatis在加载这些关联文件时也是从classpath下直接找的,所以都是配置的是classpath的相对路径 -->
<!-- MyBatis 将按照下面的顺序来加载属性。 -->
<!-- 1-在 properties 元素体内定义的属性首先被读取。 -->
<!-- 2-然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。即配置文件会覆盖属性配置的属性。 -->
<!-- 3-最后读取parameterType传递的属性,它会覆盖已读取的同名属性。 -->
<!-- 建议: 不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。 -->
<!-- 并且在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX -->
<!-- resource取值是相对于classpath根路径而言的 -->
<properties resource="mybatis/db.properties">
<!--properties中还可以配置一些属性名和属性值,直接将对应的key-value编码到下面 -->
<!-- <property name="jdbc.driver" value=""/> -->
</properties>
<!-- 设置mybatis全局参数 全局配置参数,可以等到需要时再设置 -->
<!-- <settings></settings> -->
<settings>
<!-- 全局定义mybatis对于null值类型的转换,转换为VARCHAR类型,取值有三种:NULL,VARCHAR,OTHER,不配置则默认是OTHER。 -->
<!-- 如果不全局指定,就要在对应的statementid中对单独字段进行jdbcType的指定,否则当该statementid遇到insert语句的时候 -->
<!-- 一旦传入statementid 的参数是null值的话就会报类型转换错误:Invalid column type:1111。 -->
<!-- <setting name="jdbcTypeForNull" value="NULL"/> -->
<!-- 定义mybatis的日志框架:STDOUT_LOGGING是mybatis自己实现的日志框架,对应的实现类是StdOutImpl -->
<!-- 注意如果此处不显示指定mybatis需要的实现日志框架类型,则mybatis总是按照顺序加载下面的日志实现类,如果找不到将报错: -->
<!-- SLF4J Apache Commons Logging Log4j 2 Log4j JDK logging -->
<!-- 一般配置STDOUT_LOGGING,即使用mybatis自己的日志框架,但如果要使用Log4j的配置,或者想让log4j生效,需要改成LOG4J, -->
<!-- 其他的日志框架同理。 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启mybatis的二级缓存配置,默认为true开启。如果此处设置为false,则所有mapper中的二级缓存即便配置开启也不生效。 -->
<setting name="cacheEnabled" value="true"/>
<!-- mybatis返回空字段,设置此属性为true,则mybatis每次执行完查询后,返回的 list<Map<String,Object>>中当字段查询值为null的时候, -->
<!-- 也会将字段名和字段值存储在map的entry中 -->
<setting name="callSettersOnNulls" value="true"/>
<!-- 打开延迟加载 的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 别名定义 方便在mapper.xml中使用别名就能够直接设置输入参数和输出参数的类型,而不再使用更长的全限定名 -->
<typeAliases>
<!-- 针对单个别名定义 -->
<!-- type:类型的路径 -->
<!-- alias:别名 -->
<!-- 因为下面使用了package标签实现批量的别名定义,所以单个别名定义先注释掉 -->
<!-- <typeAlias type="demo00.mybatis.model.common.po.User" alias="user"/> -->
<!-- 批量别名定义 指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以) -->
<package name="zeh.mybatis.code00common"/>
<package name="zeh.mybatis.code09mybatisspring.pojo"/>
</typeAliases>
<!-- 配置分页插件拦截器,拦截mybatis发送给数据库的sql,并设置分页参数 -->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 配置对应数据库的方言:4.0.0以后版本可以不设置该参数 ,mybatis会自动确认使用的数据库。 -->
<!-- <property name="dialect" value="oracle"></property> -->
</plugin>
</plugins>
<!-- 数据源(连接池)和数据库事务配置 -->
<!-- 和spring整合后 environments配置将废除 -->
<!-- mybatis本身还在SqlMapConfig.xml文件中提供了数据源和事务的配置,和spring整合后这些配置将全部由spring进行配置 -->
<!-- mybatis的事务和数据源是由mybatis自己实现的,没有使用dbcp,c3p0,odbc等第三方数据源等 -->
<!-- 当然,整个environments标签是在单独使用mybatis的时候才会使用;真正的项目中都是交给spring容器进行管理的。 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理,事务控制由mybatis实现 -->
<transactionManager type="JDBC"/>
<!-- 数据库连接池,由mybatis管理。下面对数据源的value的设置是从上面引用的properties配置文件中进行OGNL表达式读取的 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 配置mapper映射文件,即在当前sqlmap-config.xml中引入配置的mapper.xml -->
<mappers>
<!-- mapper代理方式开发dao遵循的规范: -->
<!-- 1.在mapper.xml中namespace等于mapper接口地址。 -->
<!-- 2.mapper.java接口中的方法名和mapper.xml中statement的id一致 。 -->
<!-- 3.mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。 -->
<!-- 4.mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。 -->
<!-- 5.如果要在sqlmapconfig.xml全局配置文件中通过class属性加载映射文件的话,还需要mapper.java接口 -->
<!-- 和mapper.xml配置文件同名且放在同一个包路径下。 -->
<!-- 否则,单独使用代理开发(全局配置中不使用class进行依赖引用的话)是不需要遵守第5点的 。 -->
<!-- 引用mapper方式1:通过resource方式引用mapper映射文件,映射文件的路径一定要区分清楚,resource是相对于classpath根路径而言的。 -->
<!-- 不论是mybatis原始方式开发dao模式还是mapper代理方式开发dao,都可以使用resource属性加载指定的映射文件mapper.xml。 -->
<!-- 但是mapper代理方式还能继续使用下面的class属性加载映射文件,还能使用package标签批量加载mapper代理方式的批量文件。 -->
<!-- 如果是mapper代理方式开发Dao,则使用resource方式引用mapper代理文件,不需要遵守上面的第5点。 -->
<mapper resource="mybatis/mapper/mybatis-first-base-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-first-foreach-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-first-if-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-proxy-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-proxy-mappingfind-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-yuanshi-base-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-yuanshi-cache-mapper.xml"/>
<mapper resource="mybatis/mapper/mybatis-yuanshi-pagehelper-mapper.xml"/>
<!-- 引用mapper方式2:通过class属性加载mapper动态代理方式开发的dao接口;上面的5点都必须遵守 -->
<!-- <mapper class="mybatis.model.dao.proxy.UserDaoMapper"/> -->
<!-- 引用mapper方式3:批量加载mapper代理的方式:上面的5点都必须遵守。 -->
<!-- 指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载 -->
<!-- <package name="mybatis.model.dao.proxy"/> -->
<!-- 扩展:mybatis的注解方式开发。 -->
<!-- 原始dao方式需要开发dao接口和实现类、配置sqlmap-config.xml全局配置文件、 配置与接口对应的mapper.xml(映射statementid)。 -->
<!-- mapper代理方式只需要开发dao接口、配置sqlmap-config.xml全局配置文件、 配置与接口对应的mapper.xml(映射statementid)。 -->
<!-- 注解方式在mapper代理方式的基础上进一步简化,只需要开发dao接口、配置sqlmap-config.xml全局配置文件。 -->
<!-- 注意:注解开发实际上是对mapper代理方式中mapper.xml配置文件的零配置替代,功能和mapper.xml文件的作用一样。 -->
<!-- 注解开发时,依赖mapper只能使用class方式进行依赖。具体遵循下面规范: -->
<!-- 注解使用接口映射,所以不需要配置mapper.xml文件,直接使用<mapper class=" "/>标签,直接配置接口的全类名(包名+接口名)即可。 -->
<!-- 注册mapper接口,使用注解开发则不需要mapper.xml映射文件。 -->
<mapper class="zeh.mybatis.code05annotation.annotation.dao.UserDaoAnnotation"></mapper>
<mapper class="zeh.mybatis.code05annotation.annotation2.UserDaoAnnotation2"/>
<!-- 扩展:mybatis 逆向工程开发;可使用 resource 加载mapper,也可使用 class 加载mapper。 -->
<mapper class="zeh.mybatis.code06nixianggongcheng.demo01.mapper.ZehUserPrimaryMapper"/>
<mapper class="zeh.mybatis.code06nixianggongcheng.demo01.mapper.ZehUserMapper"/>
</mappers>
</configuration>
2.1.3 resource下配置数据源文件
数据源配置文件名称、位置可以自定义,只需要放置在resource下即可,在mybatis的全局配置文件中,配置数据源的部分需要引用该文件。
mybatis/db.properties
# oracle 数据库连接配置
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl
jdbc.username=oracle
jdbc.password=oracle
2.1.4 resource下配置mapper.xml
mapper.xml是需要在mybatis的全局配置文件中进行引用的,引用后才能访问到。
mybatis/mapper/mybatis-first-base-mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="first">
<!-- 添加用户 -->
<insert id="insertUserFirst" parameterType="zeh.mybatis.code00common.po.singleton.CommonPo">
<!-- 将插入mysql中的自增主键返回,返回到user对象中: -->
<!-- SELECT LAST_INSERT_ID()函数:是mysql的内置函数,返回auto_increment 自增列新记录id值。 -->
<!-- 通过该函数得到刚insert进去记录的主键值,只适用于自增主键 。 -->
<!-- selectKey标签作用:获取一个值然后设置到parameterType指定类型的某个属性上。 -->
<!-- 注意:selectKey只是获取一个key值然后设置到指定的属性中去,这个key一般是通过各个数据库返回的主键, -->
<!-- 也可以自己指定statement语句实现一个子sql来返回一个值设置到属性中去。反正就是为了获取一个值设置到parameterType的属性中。 -->
<!-- selectKey标签一般都是包含在insert,update,delete标签中的。 -->
<!-- 顺序必须在insert操作之后,才能得到刚才insert进去的自增主键的值,即order="AFTER" -->
<!-- keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性 -->
<!-- order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序,即在insert操作之后执行该函数 -->
<!-- 由于mysql的自增原理执行完insert语句之后才将主键生成,所以这里 selectKey的执行顺序为after。 -->
<!-- resultType:指定SELECT LAST_INSERT_ID()的结果类型,即返回的主键是什么类型 -->
<!-- 如果mysql设置了自增主键,那么mybatis在向statementId传递入参对象时,id必须设置为null,不能有值;一旦有值的话将优先采用上送的id作为主键,而不会采用自增主键入库 -->
<!-- 本例中的users表没有设置主键,所以下属方案暂时注释掉 -->
<!-- <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID() </selectKey> -->
<!-- mybatis对null值转换报错的处理: -->
<!-- 如果mybatis在执行insert操作时候传入的参数有null值的话(注意只有null值,空字符可以正常插入,不会报类型转换错误),那么下面 -->
<!-- 使用OGNL表达式读取参数的时候,mybatis会报错,即类型转换错误:Invalid column type:1111。 -->
<!-- 原因是,对于值为null的字段,如果不指定jdbcType的话,mybatis自动将null类型转换成Other,Other对于oracle是不识别的类型,所以对于null值 -->
<!-- 必须显式指定jdbcType,否则mybatis将报无效的列类型。(注意,除了null值,对于其他的类型mybatis都自动做了转换!) -->
<!-- 还有一种解决方式,直接在sqlmap-config.xml全局配置文件中配置settings参数,jdbcTypeForNull=VARCHAR -->
<!-- mybatis的OGNL表达式的底层原理(反射): -->
<!-- 使用#{}占位,采取OGNL表达式获取(get)到po对象中的指定成员值;OGNL表达式底层采用反射方式 -->
<!-- insert into zeh_user(id,name,age,sex,hobby) values(#{id},#{name},#{age},#{sex,jdbcType=VARCHAR},#{hobby}) -->
insert into zeh_user(id,name,age,sex,hobby)
values(#{id},#{myName},#{age},#{sex,jdbcType=VARCHAR},#{hobby})
<!-- 将插入mysql中的非自增主键通过uuid()函数返回 -->
<!-- 先通过uuid()查询到主键,将主键 输入 到sql语句中。 -->
<!-- uuid()语句在insert语句之前执行。 -->
<!-- 使用场景:当使用mysql的uuid()生成主键时使用。 -->
<!-- 执行过程: -->
<!-- 首先通过uuid()函数得到主键,将主键设置到CommonPo对象的id属性中 -->
<!-- 其次在insert执行时,从user对象中取出id属性值 -->
<!-- 因为本案例使用oracle数据库,因此无法使用uuid(),注释掉以作笔记 -->
<!-- <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
SELECT uuid()
</selectKey>-->
<!-- insert into zeh_user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})-->
</insert>
</mapper>
(1)parameterType:指定输入参数类型是对应的pojo对象(包括用户信息)。
(2)#{}中指定pojo的isXXX或者getXXX中的XXX,根据XXX反射对应的getXXX或者isXXX方法,从而获取pojo中的属性值。如果不存在getXXX或者isXXX方法,则直接反射XXX的属性值;如果XXX属性也不存在,则报错。
不论是#{}还是${},mybatis都是通过OGNL表达式获取对象的属性值,即属性名.属性名.属性名…的方式(本质应该是getXXX或者isXXX中的XXX.XXX.XXX)。
(3)insert操作不需要指定resultType。
(4)insert操作指定主键id?
在进行insert操作时,如果数据库采用的是mysql自增主键、uuid等策略或者是oracle的sequeue等之类的策略时,即采用数据库的主键生成策略时,那么在insert操作时,不要通过Mybatis传入确定的主键id。如果插入对象中包含了明确的主键id,数据库的主键生成策略将失效,将会优先使用业务侧传递进来的id值。
因此,要么在mybatis的statementId中,直接去掉主键id的insert操作;
要么在业务侧传递参数时,保证传递进来的主键id是null值即可。
(5)如果采用数据库的主键生成策略,在执行insert操作后,有时候业务侧需要得到刚刚插入进去的主键id,这种情况下如何返回插入的主键呢?
通过 selectKey 标签可以完成这种能力。
2.1.5 运行
zeh.mybatis.code02mybatis.run.MybatisFirstDaoRun
package zeh.mybatis.code02mybatis.run;
import org.junit.Before;
import org.junit.Test;
import zeh.mybatis.code02mybatis.dao.MybatisFirstDao;
import java.io.IOException;
public class MybatisFirstDaoRun {
private MybatisFirstDao firstDao;
//实例化dao对象
@Before
public void newInstanceFirstDao() {
firstDao = new MybatisFirstDao();
}
@Test
public void testInsert() throws IOException {
firstDao.insertUser();
}
}
2.1.6 总结
(2)
2.2 入参中封装动态表名
2.2.1 DAO
// 入参中封装动态表名参数,执行insert操作。
public void insertUserByDynamicTableName(boolean useJingHao) throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//封装入参对象
CommonPo po = new CommonPo();
po.setId(100);
po.setName("测试动态表名接收参数");
po.setHobby("insert 语句中获取动态表名参数");
po.setAge(1000);
//设置动态表名后缀参数
po.setTableName(1);
if (useJingHao) {
//使用#{}获取动态表名参数,insert 失败,报错
sqlSession.insert("first.insertUserByDynamicTableNameUseJingHaoFirst", po);
} else {
//使用${}获取动态表名参数,insert 成功
sqlSession.insert("first.insertUserByDynamicTableNameFirst", po);
}
sqlSession.commit();
System.out.println("insert操作成功提交");
sqlSession.close();
}
2.2.2 配置mapper.xml
<!-- mybatis 的 insert 语句中获取动态表名参数,使用#{}获取动态表名参数后缀,插入失败,报错-->
<!-- 打印出的sql: insert into zeh_user_?(id,name,age,sex,hobby) values(?,?,?,?,?) -->
<insert id="insertUserByDynamicTableNameUseJingHaoFirst"
parameterType="zeh.mybatis.code00common.po.singleton.CommonPo">
insert into zeh_user_#{tableName}(id,name,age,sex,hobby)
values(#{id},#{name},#{age},#{sex},#{hobby})
</insert>
<!-- mybatis 的 insert 语句中获取动态表名参数后缀,使用${}获取动态表名参数后缀,正常插入-->
<!-- 打印出的sql: insert into zeh_user_1(id,name,age,sex,hobby) values(?,?,?,?,?) -->
<insert id="insertUserByDynamicTableNameFirst" parameterType="zeh.mybatis.code00common.po.singleton.CommonPo">
insert into zeh_user_${tableName}(id,name,age,sex,hobby)
values(#{id},#{name},#{age},#{sex},#{hobby})
</insert>
2.2.3 运行
@Test
public void testInsertByDynamicTableNameUseJingHao() throws IOException {
firstDao.insertUserByDynamicTableName(true);
}
@Test
public void testInsertByDynamicTableName() throws IOException {
firstDao.insertUserByDynamicTableName(false);
}
2.2.4 总结
2.3 测试日期类型
2.3.1 DAO
// 插入zeh_person表,主要验证日期类型:
// 1、类型使用java中sql的Date或者java.util包下的Date。
// 2、oracle数据库对应字段设置为Date类型。
public void insertZehPerson(Boolean isUtilDate) throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
ZehPerson person = new ZehPerson();
person.setId(06L);
person.setName("赵二虎");
person.setAge(19L);
person.setSex("男");
if (isUtilDate) {
//使用util包下的Date,对应的oracle字段类型为date,则存入数据表的直接就是“年月日时分秒”格式的日期类型。
Date date = new Date();
person.setBirthday(date);
} else {
//1、java.sql包下的Date要表示当前日期必须传入一个参数即毫秒数(自1970年至今的毫秒数)
//2、或者传入年月日
//3、java.sql.date 没有无参的构造函数,必须传值,java.util.date可以使用无参构造函数,获取的是当前的时间。
//4、如果以传入年月日的方式构造java.sql.Date,则java.sql.date的时间格式是年-月-日,没有时分秒部分。强制从sql.Date中获取时分秒,会抛异常。
// sql.Date可以进行getTime,获取的time也只是表示当前日期的毫秒数,而不包含时分秒在内。
//5、如果传入毫秒数的方式构造java.sql.date,则和java.util.date一样都表是当前时间,入库后也是年月日时分秒,两者就没有区别了。
// java.sql.Date date = new java.sql.Date(System.currentTimeMillis());
java.sql.Date date = new java.sql.Date(new Date().getYear(), new Date().getMonth(), new Date().getDay());
//强制从sql包的Date对象获取时分秒,则抛出异常。
// date.getHours();
// date.getMinutes();
// date.getSeconds();
long currentMis = date.getTime();
System.out.println("毫秒数:" + currentMis);
System.out.println("当前时间毫秒数:" + System.currentTimeMillis());
person.setBirthday(date);
}
sqlSession.insert("first.insertZehPersonFirst", person);
sqlSession.commit();
System.out.println("insert操作成功提交");
sqlSession.close();
}
2.3.2 配置mapper.xml
<!-- 1.插入 zeh_person 表,主要验证Date类型的插入。 -->
<!-- 2.建议在代码层面设置date类型直接使用java.util.date。 -->
<!-- 3.对于oracle数据库,对应的字段必须是date类型,应用中的java.util.date类型才能按照年月日时分秒的方式插入到表中。 -->
<!-- 4.对于mysql,数据库的字段必须是timestamp或者datetime类型,应用中的java.util.date类型才能按照年月日时分秒的方式插入到表中。-->
<!-- 如果mysql对应的字段是date类型的话,则数据库中插入后只会显示年月日,而没有时分秒。-->
<!-- 5.不论是oracle还是mysql都可以根据数据库中的date类型或者timestamp类型进行排序。-->
<!-- 6.insert语句没有where条件。传入一个对象,可以插入目标表中的所有字段,也可以插入部分对应的字段。 -->
<!-- 7.合理使用if标签、foreach标签等对传入参数进行遍历和非空判断,从而规避某些空字段的插入(对于更新操作也是一样,具体参照逆向工程的mapper)。-->
<insert id="insertZehPersonFirst" parameterType="zeh.mybatis.code00common.entity.ZehPerson">
insert into zeh_person(ID,NAME,AGE,SEX,BIRTHDAY)
values(#{id},#{name},#{age},#{sex},#{birthday})
</insert>
2.3.3 运行
@Test
public void testInsertZehPersonByUtilDate() throws IOException {
firstDao.insertZehPerson(Boolean.TRUE);
}
@Test
public void testInsertZehPersonBySqlDate() throws IOException {
firstDao.insertZehPerson(Boolean.FALSE);
}
2.3.4 总结
2.4 查询记录
2.4.1 DAO
public void selectZehPersonFirst() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<ZehPerson> list = sqlSession.selectList("first.selectZehPersonFirst");
Iterator<ZehPerson> iterator = list.iterator();
System.out.println("根据id查询到的zeh_person表中的record如下:");
while (iterator.hasNext()) {
ZehPerson zehPerson = iterator.next();
Long id = zehPerson.getId();
String name = zehPerson.getName();
Long age = zehPerson.getAge();
String sex = zehPerson.getSex();
Date birthday = zehPerson.getBirthday();
System.out.println("id = " + id + ";name = " + name + ";age = " + age + ";sex = " + sex + ";birthday = " + birthday);
}
sqlSession.close();
}
2.4.2 配置mapper.xml
<!--1.如果只查询id,name两个字段,则定义的pojo可以只定义这两个映射属性即可。 -->
<!--2.一句话,表对应的实体映射字段需要和对应的 statement id 查询的字段保持一致即可。 -->
<!--3.parameterType和resultType也可以只写对应实体的别名,配置后默认的别名为对应实体类的类名,首字母大小写皆可。 -->
<select id="selectZehPersonFirst" parameterType="zeh.mybatis.code00common.entity.ZehPerson"
resultType="zehPerson">
select id,name from zeh_person
</select>
2.4.3 运行
@Test
public void testSelectZehPersonFirst() throws IOException {
firstDao.selectZehPersonFirst();
}
2.4.4 总结
2.5 查询操作
2.5.1 DAO
public void selectZehPersonSecond() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Long id = sqlSession.selectOne("first.selectZehPersonSecond");
System.out.println(id);
System.out.println(id == null);
sqlSession.close();
}
2.5.2 配置mapper.xml
<select id="selectZehPersonSecond" parameterType="zeh.mybatis.code00common.entity.ZehPerson"
resultType="long">
select id from zeh_person where id = 100
</select>
2.5.3 运行
@Test
public void testSelectZehPersonSecond() throws IOException {
firstDao.selectZehPersonSecond();
}
2.5.4 总结
2.6 select语句使用动态表名后缀
2.6.1 DAO
// 功能描述: select 语句中使用动态表名后缀
// 结论:动态表名参数,不论参数类型是Integer还是String,都必须使用${}获取参数,因为#{}会预编译。
public void selectZehPersonByDynamicTableNameFirst(boolean useJingHao) throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<CommonPo> list = null;
ZehPersonPo personPo = new ZehPersonPo();
//设置动态表名后缀
personPo.setTableName(1);
if (useJingHao) {
//使用 #{} 获取动态表名后缀,select 失败,报错
list = sqlSession.selectList("first.selectZehPersonByDynamicTableNameUseJingHaoFirst", personPo);
} else {
//使用 ${} 获取动态表名后缀,select 成功
list = sqlSession.selectList("first.selectZehPersonByDynamicTableNameFirst", personPo);
}
Iterator<CommonPo> iterator = list.iterator();
System.out.println("根据id查询到的zeh_person表中的record如下:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
sqlSession.close();
}
2.6.2 配置mapper.xml
<!-- mybatis 的 select 语句中获取动态表名后缀参数,使用#{}获取动态表名参数后缀,查询失败 -->
<select id="selectZehPersonByDynamicTableNameUseJingHaoFirst"
parameterType="zeh.mybatis.code00common.entity.ZehPerson"
resultType="zeh.mybatis.code00common.entity.ZehPerson">
select id,name from zeh_person_#{tableName}
</select>
<!-- mybatis 的 select 语句中获取动态表名后缀参数,使用${}获取动态表名参数后缀,查询成功 -->
<select id="selectZehPersonByDynamicTableNameFirst"
parameterType="zeh.mybatis.code00common.entity.ZehPerson"
resultType="zeh.mybatis.code00common.entity.ZehPerson">
select id,name from zeh_person_${tableName}
</select>
2.6.3 运行
@Test
public void testSelectZehPersonByDynamicTableNameUseJingHaoFirst() throws IOException {
firstDao.selectZehPersonByDynamicTableNameFirst(true);
}
@Test
public void testSelectZehPersonByDynamicTableNameFirst() throws IOException {
firstDao.selectZehPersonByDynamicTableNameFirst(false);
}
2.6.4 总结
2.7 根据Id查询多条记录
2.7.1 DAO
// 根据id查询用户信息,得到多条记录结果
public void findUserById() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录,selectList查询一堆记录。
List<ZehUser> list = sqlSession.selectList("first.findUserByIdFirst", 3);
Iterator<ZehUser> iterator = list.iterator();
System.out.println("根据id查询到的ZEH_USER表中的record如下:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
sqlSession.close();
}
2.7.2 配置mapper.xml
<!-- 1.需求:通过id查询用户表的记录,很明显需要传入一个简单类型即String或者Integer类型的id参数。 -->
<!-- 2.在mapper映射文件中配置很多sql语句:通过 select 标签执行数据库查询。 -->
<!-- 3.标签属性解释: -->
<!-- (1)、id:标识映射文件中的 sql,将sql语句封装到mappedStatement对象中,所以将id称为statement的id。 -->
<!-- (2)、parameterType:指定输入参数的类型,这里指定int型,mybatis默认支持很多类型别名用于指定parameterType和resultType:比如int映射java.lang.Integer,string映射java.lang.String等。 -->
<!-- (3)、resultType:指定sql输出结果所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象,即便查询出的是集合list,也仅仅指定的是list里元素的类型。 -->
<!-- (4)、#{}表示一个占位符号,mybatis自动将传入的string类型加上单引号。该方式传递进来的数据会预编译成占位符?,和其组成的sql语句一同被jdbc预编译。 -->
<!-- (5)、#{id}:其中的id表示接收输入的参数,参数名称就是id。如果输入参数是简单类型,#{}中的参数名可以任意,可以是value或其它名称,通过OGNL直接获取简单类型的值。 -->
<!-- 4.如果parameterType输入的不是简单类型,那么#{}中必须指明的是map中的key值或者pojo对象中的getXXX或者isXXX的XXX值;如果没有getXXX或者isXXX方法,则必须指定对应的成员名称。 -->
<!-- 5.mybatis的 if 标签只能判断list,pojo,或者map,通过获取属性值或者key来判断。对于只传递一个简单类型的判断只能使用 _parameter 默认参数或者value别名进行判断表示。-->
<!-- 6._parameter:表示当前的 statementId 接收的参数,它表示接收的任何参数对象,可以表示简单类型,也可以表示pojo,list,map等任何输入对象。-->
<!-- 7.正因为对简单类型只能使用_parameter这个默认参数进行if判断,所以一般是通过map去包装这些简单类型,哪怕只需要一个简单类型,也建议通过map或者po去包装。 -->
<!-- 8.总结,parameterType不管是否显式设置,mybatis都会根据此时传入进来的类型自动转换成对应的类型进行接收。 -->
<!-- 9.换言之,如果设置了parameterType为String,或者没有指定parameterType,此时传入的是int,则接受的参数就是int类型。不过建议传入参数和parameterType保持一致的类型。 -->
<!-- 10.#{}和${}获取输入参数原理如下:-->
<!-- (1)、简单类型直接赋值;-->
<!-- (2)、map对象直接根据key遍历传入的map对象执行get()方法获取value;-->
<!-- (3)、pojo根据isXXX或者getXXX的XXX进行方法的反射从而获取值;如果不存在getXXX或者isXXX方法,则直接反射XXX的属性值;如果XXX属性也不存在,则报错。-->
<!-- (4)、list或者array需要使用foreach进行遍历,然后获取list中的item,如果item是简单类型,则遵守(1);是map则遵守(2);是pojo则遵守(3)。-->
<!-- 11、select查询语句,mybatis规则是:如果查询的结果是pojo,查询不到则直接返回null值;如果查询的结果是list(不管list中元素是啥),查询不到,返回空的List对象。-->
<select id="findUserByIdFirst" parameterType="java.lang.String"
resultType="zeh.mybatis.code00common.entity.ZehUser">
select * from zeh_user a
where 1 = 1
<!-- 简单类型使用自主命名 qitamingcheng 用于if判断则报错 -->
<!--<if test="qitamingcheng != null">-->
<!-- 对于简单类型的if判断只能通过默认的_parameter 参数 或者value进行,而不能自主命名参数名称。-->
<!--<if test="_parameter != null">-->
<if test="value != null">
and a.id=#{qitamingcheng}
</if>
<!-- </if>-->
<!-- </if>-->
</select>
2.7.3 运行
@Test
public void testSelectUserByIdTest() throws IOException {
firstDao.findUserById();
}
2.7.4 总结
2.8 根据name查询多条记录
2.8,1 DAO
// 根据用户的name查询多条record
public void findUserByName() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<CommonPo> list = sqlSession.selectList("first.findUserByNameFirst", "于瑞");
Iterator<CommonPo> iterator = list.iterator();
System.out.println("根据name查询到的ZEH_USER表中的record如下:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
sqlSession.close();
}
2.8.2 配置mapper.xml
<!-- 1.根据用户名称模糊查询用户信息,可能返回多条。 -->
<!-- 2.resultType:指定查询的单条记录所映射的java对象类型。简单类型或者list,pojo,map等。如果返回是list只需要指定其中的pojo类型即可。 -->
<!-- 3.${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。不加任何修饰,意味着传进来是什么就是什么,对于字符串连单引号都不会加,不会转义也不会预编译。 -->
<!-- 4.使用${}拼接sql,引起 sql注入。因为${}的方式传入的参数不会被预编译成占位符?而是和sql一起发送给数据库,由数据库统一进行编译(此时jdbc将不替数据库进行预编译)。 -->
<!-- 5.${value}:接收输入参数的内容,如果传入类型是简单类型,${}中只能使用value,而不像#{}中可以使用任意名称对简单类型进行获取。 -->
<!-- 6.像本例中这种直接在sql中编写字符串的,要么使用${}这种形式拼接字符串;要么就只能使用#{}进行整体占位。 -->
<!-- 7.本例先使用${}进行原封不动的拼接(注意${}不会为字符串加上'')。 -->
<select id="findUserByNameFirst" parameterType="java.lang.String"
resultType="zehUser">
select * from zeh_user where name like '%${value}%'
<!-- 下面方式直接使用${}取值是不对的,因为${}取出来的值是不加任何修饰的,对于字符串连引号也不会加,所以如果采用${}取值的话需要手动加上单引号 -->
<!-- select * from zeh_user where name = ${value} -->
<!-- 使用${}必须加上单引号 -->
<!-- select * from zeh_user where name = '${value}' -->
</select>
2.8.3 运行
@Test
public void testSelectUserByNameTest() throws IOException {
firstDao.findUserByName();
}
2.8.4 总结
2.9 select语句不指定parameterType和resultType
2.9.1 DAO
// 根据id查询用户信息,得到多条记录结果
// 指定的mapper.xml配置文件中对应的 statementId 中没有配置parameterType输入参数类型和resultType返回类型
// 测试mybatis在未配置parameterType和resultType时是否默认指定的输入类型和输出类型都是hashMap
// 结论:mapper.xml的 statementId 中可以不配置parameterType,此时默认传入的类型mybatis会自动匹配,换言之,
// 你传入的是Integer类型的参数变量,mybatis的statementId的parameterType就是int类型(之前以为默认不写parameterType的话默认的是map类型,OGNL通过key取值,这是不对的,默认是自动匹配输入参数类型)。
// 但是,对于select节点的 statementId 必须配置resultType,尽管默认的是map类型,也必须显示配置为map,否则无法执行。
public void findUserByIdTestNoType() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Integer flag = 3;
List<Map<String, Object>> list = sqlSession.selectList("first.findUserByIdNoTypeFirst", flag);
Iterator<Map<String, Object>> iterator = list.iterator();
System.out.println("根据id查询到的ZEH_USER表中的record如下:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
sqlSession.close();
}
2.9.2 配置mapper.xml
<!-- statementId 不配置parameterType和resultType,观察默认的输入参数类型和输出参数类型。 -->
<!-- 测试结论: -->
<!-- 1.输入类型parameterType不论是否进行显式配置,mybatis都会自动转型,传入的是什么类型,此处就会转换成什么类型。 -->
<!-- 2.如果不指定parameterType,当传入的是对象或者是map类型的话,仍旧需要使用 OGNL 取值,按照key值或者pojo中的isXXX或者getXXX的XXX进行取值。 -->
<!-- 3.尽管默认的输出是List<Map<String,Object>>,对于select节点的 statementId 必须配置resultType为map,不配置将报错。 -->
<!-- 4.如果resultType指定了map,则List<Map<String,Object>>为mybatis的默认输出,此时是按照数据表中的每一行中的列名作为key,值作为value进行组合的。 -->
<!-- 备注:一个List<Map<String,Object>>表示数据表中一堆记录,其中每一个map对象表示一条记录(默认封装的map中注意数据表中的列名是严格区分大小写的,必要时需要在代码中转换)。 -->
<select id="findUserByIdNoTypeFirst" resultType="map">
select * from zeh_user where id=#{flagtest}
</select>
2.9.3 运行
@Test
public void notSetParamType() throws Exception {
firstDao.findUserByIdTestNoType();
}
2.9.4 总结
2.10 根据id删除
2.10.1 DAO
// 根据id删除user表中的record记录,id为null的也执行删除操作
public void deleteUser() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int deleteResult = sqlSession.delete("first.deleteUserFirst", 154);
sqlSession.commit();
System.out.println("delete操作成功提交:" + deleteResult);
sqlSession.close();
}
2.10.2 配置mapper.xml
<!-- 1.删除用户 。-->
<!-- 2.根据id删除用户,需要输入id值,或者id is null的record。 -->
<delete id="deleteUserFirst" parameterType="java.lang.Integer">
delete from zeh_user
where 1=1 and id=#{id} or id is null
</delete>
2.10.3 运行
@Test
public void testDelete() throws IOException {
firstDao.deleteUser();
}
2.10.4 总结
2.11 更新操作
2.11.1 DAO
// 根据id更新users表的记录,id必须存在。
// update操作类似于insert,必须首先封装user对象。
public void updateUser() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
ZehUser zehUser = new ZehUser();
zehUser.setId(02L);// id必须设置,否则数据库不知道更新哪一条记录
zehUser.setName("修改后");
zehUser.setAge(100L);
zehUser.setSex("男");
zehUser.setHobby("么有兴趣");
// 将封装好的user对象发送给数据库
sqlSession.update("first.updateUserFirst", zehUser);
sqlSession.commit();
System.out.println("update操作成功提交");
sqlSession.close();
}
2.11.2 配置mapper.xml
<!-- 1.根据id更新用户。 -->
<!-- 2.需要传入用户的id 。-->
<!-- 3.需要传入用户的更新信息 。-->
<!-- 4.parameterType指定user对象,包括 id和更新信息,注意:id必须存在。 -->
<!-- 5.#{id}:从输入user对象中获取id属性值。 -->
<!-- 6.任何一个sql语句,使用mybatis都必须分析出其中的输入参数和输出参数到底是什么,应该怎么配置,是简单java类型,还是pojo自定义对象还是hashMap等集合。 -->
<!-- 7.合理使用if标签,foreach标签等对传入参数进行遍历和非空判断,从而规避某些空字段的更新;在update语句时,尤其注意有些字段传入为null或者空字符,而原本有默认值,-->
<!-- 如果不合理使用if进行判空,或者全量指定了所有全量字段的更新,则会覆盖掉表中原有字段的默认值,造成数据错误(具体参照逆向工程的mapper)。-->
<update id="updateUserFirst" parameterType="zeh.mybatis.code00common.entity.ZehUser">
<if test="_parameter != null">
update zeh_user
<!-- set 标签和set关键字一样,如果需要对set的字段进行if等标签处理并且去掉最后一个,,则用set标签代替set关键字;否则直接使用set关键字即可。 -->
<set>
<if test="name != null">
name=#{name},
</if>
<if test="age != null">
age=#{age},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="hobby != null">
hobby=#{hobby},
</if>
</set>
where id=#{id}
</if>
</update>
2.11.3 运行
@Test
public void testUpdate() throws IOException {
firstDao.updateUser();
}
2.11.4 总结
文档信息
- 本文作者:Marshall