mybatis基础系列(二)——基础语法、别名、输入映射、输出映射
增删改查
mapper根节点及其子节点
mybatis框架需要读取映射文件创建会话工厂,映射文件是以<mapper>作为根节点,在根节点中支持9个元素,分别为insert、update、delete、select(增删改查);cache、cache-ref、resultMap、parameterMap、sql。如下图:
命名空间
<mapper>根节点有个属性namespace,作用是对sql语句进行分类化管理。
select节点
占位符#{}
一个<select>代表一条查询语句,<select>常用属性有id,parameterType,resultType。<select>节点的内容为sql语句,其语法与平常写的sql语句相似,不同的地方是条件参数可以通过占位符#{}替换。例如:
sql语法:
SELECT * FROM t_emp WHERE empno=’7369’
mybitis中的语法:
SELECT * FROM t_emp WHERE empno=#{empno}
id:标志映射文件中的sql,通常id也称为statement的id。id的值就是xxxMapper.java中的方法名。
parameterType:执行sql语句中的输入参数的类型。
resultType:指定sql输出结果映射成java类型的对象。
#{}:表示一个占位符
#{id}:其中id表示接收输入的参数,参数名称就是id。#{}中的参数可以是任意对象。
示例与运行问题
EmpMapper.xml
<mapper namespace="com.itpsc.mapper.EmpMapper" > <select id="queryById" parameterType="int" resultType="com.itpsc.entity.Emp"> SELECT * FROM t_emp WHERE empno=#{empno} </select> </mapper>
//EmpMapper.java public interface EmpMapper extends BaseMapper<Emp> { Emp queryById(Integer empno); } //EmpService.java public interface EmpService { Emp queryById(Integer empno); } //EmpServiceImpl.java @Service public class EmpServiceImpl extends ServiceImpl<EmpMapper,Emp> implements EmpService { public EmpServiceImpl() { super(); }; public EmpServiceImpl(EmpMapper mapper) { this.baseMapper = mapper; }; @Override public Emp queryById(Integer empno) { return this.baseMapper.queryById(empno); } } //EmpTests.java @RunWith(SpringRunner.class) @SpringBootTest public class EmpTests { @Resource private EmpService empService; @Test public void contextLoads() { } @Test public void testQueryById() { System.out.println(empService.queryById(7369)); } }
运行报错:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.itpsc.mapper.EmpMapper.queryById
找不到xml,发现在idea编译后的classes路径下并没有相应的XML文件。
因为IDEA在编译的时候忽略掉了XML文件,一个解决方法是将所有的XML文件移动到Resource文件夹下,这样在编译的时候就会将XML文件一起。
另一个方法是配置Maven不过滤src/main/java目录下的.properties文件和.xml文件。
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources>
再次编译就看到了classes目录下有xml文件了。
运行test后输出:
Emp{empno=7369, ename='SMITH', job='CLERK', mgr=7902, hiredate=Wed Dec 17 00:00:00 CST 1980, sal=800.0, comm=null, deptno=20}
拼接${}
${}:用来拼接sql字符串,将接收到的参数内容不加任何修饰拼接在sql语句中。
sql语法:
SELECT * FROM t_emp WHERE ename LIKE 'SMITH';
mybitis中的语法:
<select id="queryLikeName" parameterType="String" resultType="com.itpsc.entity.Emp"> SELECT * FROM t_emp WHERE ename LIKE '${_parameter}' </select>
注意
如果传入的参数类型为String类型,则参数名需统一修改为_parameter,不能将参数设为bean里的名称。
否则运行报错为:There is no getter for property named 'preCode' in 'class java.lang.String
insert节点
一个<insert>代表一条insert语句,和<select>节点一样,<insert>其语法与平常写的sql语句相似,不同的地方是条件参数可以通过占位符#{}替换。
sql语法:
insert into t_emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values(7100,’itpsc’,’developer’,7902,’1980-12-10’,1000.00,200.00,20)
mybitis中的语法:
<insert id="add" parameterType="com.itpsc.entity.Emp"> insert into t_emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values(#{empno},#{ename},#{job},#{mgr},#{hiredate,jdbcType=DATE},#{sal},#{comm},#{deptno}) </insert>
注意日期类型
mybatis日期类型的字段,要加jdbcType=DATE。否则会报错:There is no getter for property named 'hirdate' in 'class com.itpsc.entity.Emp'。
Mybatis中javaType和jdbcType对应关系
JDBC Type Java Type CHAR String VARCHAR String LONGVARCHAR String NUMERIC java.math.BigDecimal DECIMAL java.math.BigDecimal BIT boolean BOOLEAN boolean TINYINT byte SMALLINT short INTEGER int BIGINT long REAL float FLOAT double DOUBLE double BINARY byte[] VARBINARY byte[] LONGVARBINARY byte[] DATE java.sql.Date TIME java.sql.Time TIMESTAMP java.sql.Timestamp CLOB Clob BLOB Blob ARRAY Array DISTINCT mapping of underlying type STRUCT Struct REF Ref DATALINK java.net.URL
insert与非自增主键返回
有时候新增记录之后,需要这条新增记录的主键,以便业务使用,但是新增之后再将其查询出来明显不合理,效率也变低了。mybatis可以将insert的记录的主键返回。使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,长度设置成35位。
mybatis的<selectKey>可以帮我们实现。Insert之前先通过uuid()查询到主键,将主键输入到sql语句中。
UserMapper.xml
<mapper namespace="com.itpsc.mapper.UserMapper" > <insert id="adduser" parameterType="com.itpsc.entity.User"> <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String"> SELECT uuid() </selectKey> insert into t_user(id,name,password,phone) values(#{id},#{name},#{password},#{phone}) </insert> </mapper>
测试 @Test public void testAddUser() { User user = new User(); //user.setId(1L); user.setName("uuid name1"); user.setPassword("98764"); user.setPhone("13877711111"); System.out.println(userService.adduser(user)); System.out.println(user.getId()); } 输出: 1 9a64919e-b02f-11e8-8b1f-f48e38ec6bad
insert与自增主键返回
再将user表中的id字段修改为Bigint类型,并设为自增。User.java中id修改为Long类型。通过mysql函数LAST_INSERT_ID()获取到刚插入记录的自增主键,是insert之后调用此函数。
<insert id="getIdAfterAdduser" parameterType="com.itpsc.entity.User"> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long"> SELECT LAST_INSERT_ID() </selectKey> insert into t_user(name,password,phone) values(#{name},#{password},#{phone}) </insert>
@Test public void getIdAfterAdduser() { User user = new User(); user.setName("auto add id"); user.setPassword("98764"); user.setPhone("13877711111"); System.out.println(userService.getIdAfterAdduser(user)); System.out.println(user.getId()); } 运行输出: 1 3
delete节点
sql语法:
delete FROM t_user WHERE id=3
mybitis中的语法:
delete FROM t_user WHERE id=#{id}
<delete id="delete" parameterType="java.lang.Long"> DELETE from t_user WHERE id=#{id} </delete>
update节点
<update id="updateById" parameterType="com.itpsc.entity.User"> UPDATE t_user set NAME=#{name},password=#{password},phone=#{phone} WHERE id=#{id} </update>
把整个java对象传入,要更新哪些字段sql语句决定。
占位符与拼接符小结
#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型也可以是复杂的数据类型。#{}接收对象值,通过OGNL语法(user.username)读取对象中的属性值。
${}表示一个拼接符号,会引用sql注入,不建议使用${}。${}接收输入参数,类型可以是简单类型也可以是复杂数据类型。${}接收对象值,通过OGNL语法(user.username)读取对象中的属性值。
出入参定义别名
批量定义别名
在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。
如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。
在springboot 的yml配置文件中通过type-aliases-package定义别名,在对parameterType或resultType指定的类型中就可以省略包名。
mybatis-plus: mapper-locations: "classpath:com/itpsc/mapper/**/*.xml" type-aliases-package: "com.itpsc.entity" global-config: db-column-underline: true <insert id="adduser" parameterType="User"> insert into t_user(name,password,phone) values(#{name},#{password},#{phone}) </insert>
输入映射
parameterType
前面我们学习的输入都是简单对象,如果输入参数的类型是复杂对象(包装对象),该怎么写呢。
<select id="queryList" parameterType="com.itpsc.request.EmpRequest" resultType="com.itpsc.vo.EmpVo"> SELECT * FROM t_emp WHERE deptno=#{emp.deptno} and job=#{emp.job} </select>
public interface EmpMapper extends BaseMapper<Emp> { //... EmpVo queryList(EmpRequest request); } public class EmpRequest{ private Emp emp; //其它条件 public Emp getEmp() { return emp; } public void setEmp(Emp emp) { this.emp = emp; } } public class EmpVo extends Emp{ //... }
使用parameterType进行输入映射,类型是包装对象,但是占位符里用的是被包装对象的属性。通过#{emp.deptno}取出被包装对象的deptno属性。
输出映射
resultType
运行测试方法报错:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4
返回数据类型由xxxMapper.java接口中声明的方法的返回类型和xxxMapper.xml文件共同决定。如果mapper方法返回单个对象(非集合对象),代理对象内部通过selectOne查询数据库。如果mapper方法返回集合对象,代理对象内部通过selectList查询数据库。不论是返回单一对象还是对象列表,xxxMapper.xml中的配置都是一样的,都是resultMap=”***Map”或resultType=“* .* .*”类型。
例如:
返回单个对象resultType的值为"com.itpsc.entity.Emp"
返回多个对象resultType的值也是"com.itpsc.entity.Emp"
所以将mapper方法的返回类型声明为List<Emp>即可。
使用resultType进行输出映射,只有查询出来的列名和对象中的属性名一致,该列才可以映射成功。如果查询出来的列名和对象中的属性名全部不一致,没有创建对象。只要查询出来的列名和对象中的属性有一个一致,就会创建对象。
如果我们把上面例子中的EmpVo 不继承Emp,查询出来就不创建对象。如下图
resultMap
如果查询出来的列名和对象的属性名不一致,通过定义一个resultMap对列名和对象属性名之间作一个映射关系。
1、定义resultMap
2、使用resultMap作为statement的输出映射类型
<resultMap id="userMap" type="com.itpsc.entity.Emp" > <result column="_empno" property="empnum" jdbcType="INTEGER" /> <result column="_ename" property="ename" jdbcType="VARCHAR" /> <result column="_job" property="job" jdbcType="VARCHAR" /> <result column="_mgr" property="mgr" jdbcType="INTEGER" /> <result column="_hiredate" property="hiredate" jdbcType="DATE" /> <result column="_sal" property="sal" jdbcType="REAL" /> <result column="_comm" property="comm" jdbcType="REAL" /> <result column="_deptno" property="deptno" jdbcType="INTEGER" /> </resultMap> <select id="queryById" parameterType="int"resultMap ="userMap"> SELECT empno _empno,ename _ename,job _job,mgr _mgr,hiredate _hiredate,sal _sal,comm _comm,deptno _deptno FROM t_emp WHERE empno=#{empno} </select>
本篇到此结束,下篇预告mybatis基础系列(三)——动态sql。