[Spring+SpringMVC+Mybatis]框架学习笔记(九):Mybatis主配置文件和映射文件
第9章 Mybatis主配置文件和映射文件
9.1 用Mybatis进行开发的两种方式
在正式的开发环境中用Mybatis进行开发有两种方式:
1)原始的接口和实现类的方式
缺点:
- 重复代码太多,sqlSession的操作
- statement的id硬编码将来影响维护
2)基于mapper代理的开发方式(重点)
mybatis根据一些规则自动创建dao接口的实现类的代理对象
规则:
- userMapper.xml中namespace必须指定为UserMapper(相当于第8讲案例中的IUserDao)的全限定类名
- userMapper.xml中statement(即sql语句)的id指定为UserMapper接口中对应的方法名称
- userMapper.xml中statement的输入参数类型和UserMapper中对应的方法参数类型一致
- userMapper.xml中statement的返回类型和UserMapper中的对应方法的返回类型一致
9.2 主配置文件config.xml详解
* properties标签
主要用于配置数据库的连接数据
* settings 全局的配置参数
mybatis中的运行时行为设置 ,比如缓存的开启,驼峰命名的映射,延迟加载的设置等等
* plugins
mybatis需要引入的一些插件 :分页插件pageHelper
* enviroments
环境配置,可以配置多种数据库连接
* mapper
详见mybatis-config.xml
9.3 映射文件mapper.xml详解
-
输入参数parameterType
- 简单类型的单个参数
需求:根据用户id查询用户信息 - 简单类型的多个参数
需求:通过登录名和密码验证用户是否存在 - 包装类对象作为输入参数进行查询
需求:根据界面输入的用户名称或者登录名称来查询符合条件的用户列表
- 简单类型的单个参数
-
动态sql
- if标签和where标签
- sql片段
- foreach
-
输出参数resultType/resultMap
- 简单类型的输出 Integer(可写为int,即用别名,该别名是自动设置的,不需要在主配置文件中设置) String Long(可写为long)
- 对象的输出
- HashMap的输出
- resultMap对象输出(当数据库设计时没有遵循命名规则,java设计也没有遵循命名规则时使用 --> 手动设置数据库表列名与java属性之间的对应关系)
9.4 基于mapper代理的Mybatis的Demo
9.4.1 测试数据准备
jdbc的属性配置文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis_test
jdbc.user=root
jdbc.password=123
9.4.2 User实体类、Dao接口
User实体类与8.4.2相同。
包装类UserDto:
package com.steven.mybatis.sysmanage.dto;
import com.steven.mybatis.sysmanage.entity.User;
/**
* 跟数据库M_USER表对应的实体类
* @author chenyang
*
*/
public class UserDto implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private User user;
public User getUser(){
return user;
}
public void setUser(User user){
this.user = user;
}
}
Dao接口UserMapper基本与8.4.2相同,这里增加了几个方法。
package com.steven.mybatis.sysmanage.mapper;
import java.util.List;
import java.util.Map;
import com.steven.mybatis.sysmanage.dto.UserDto;
import com.steven.mybatis.sysmanage.entity.User;
/**
* 定义用户增删改查dao接口
* @author chenyang
*
*/
public interface UserMapper {
/**
* 根据用户id获取用户对象信息
* @param userId
* @return
*/
public User getUserById(Long userId);
/**
* 通过登录名和密码来验证用户是否存在
* @param loginName
* @param password
* @return
*/
public User getUserByLoginNameAndPsd(String loginName, String password);
/**
* 通过用户实体包装类来进行查询
* @param userDto
* @return
*/
public List<User> getUserListByUserDto(UserDto userDto);
/**
* 获取所有用户数目
* @return
*/
public Integer getUserCount();
/**
* 查询所有用户数据,以map结构数据返回
* @return
*/
public List<Map<Object, Object>> getUserListMap();
/**
* 查询所有用户数据,用resultMap返回结果集
* @return
*/
public List<User> getUserListByResultMap();
/**
* 查询所有用户数据,查询所有用户对象
* @return
*/
public List<User> getUserList();
/**
* 增加用户对象
* @param user
*/
public void addUser(User user);
/**
* 删除用户对象
* @param user
*/
public void delUser(User user);
/**
* 修改用户对象
* @param user
*/
public void updateUser(User user);
}
9.4.3 Mybatis的主配置文件
<?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>
<properties resource="jdbc.properties"></properties>
<settings>
<!-- 是否开启自动检测驼峰命名规则,如USER_ID userId
即从经典的数据表列名AB_COLUMN到经典的java属性名的映射abColumn
默认是false
-->
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
</settings>
<!-- 设置别名 -->
<typeAliases>
<!-- 单个设置别名 -->
<!-- <typeAlias type="com.steven.mybatis.sysmanage.entity.User" alias="user"/> -->
<!-- 批量设置别名 -->
<package name="com.steven.mybatis.sysmanage.entity"/>
</typeAliases>
<!-- 默认引用那个数据库环境 -->
<environments default="defaultEnv">
<environment id="defaultEnv">
<!-- 事务管理方式 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据库连接四要素 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 加入所有的sql映射文件 -->
<mappers>
<!-- 第一种方式,用resource属性,注意目录用/分隔 -->
<!-- <mapper resource="com/steven/mybatis/sysmanage/mapper/UserMapper.xml"></mapper> -->
<!-- 第二种方式,用class属性应用接口类,规则:接口类和sql映射文件必须同名,然后在同一路径 -->
<!-- <mapper class="com.steven.mybatis.sysmanage.mapper.UserMapper"></mapper> -->
<!-- 第三种方式,用package标签批量自动扫描所配置的包路径的所有接口
规则:接口类和sql映射文件必须同名,然后在同一路径-->
<package name="com.steven.mybatis.sysmanage.mapper"></package>
</mappers>
</configuration>
9.4.4 对象映射文件
<?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="com.steven.mybatis.sysmanage.mapper.UserMapper">
<select id="getUserById" parameterType="long" resultType="user">
SELECT USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE FROM M_USER
WHERE USER_ID=#{ID}
</select>
<!-- 通过登录名和密码验证用户是否存在 -->
<select id="getUserByLoginNameAndPsd" resultType="user">
SELECT USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE FROM M_USER
WHERE LOGIN_NAME=#{0} AND PASSWORD=#{1}
</select>
<!-- 通过包装类UserDto的条件来查询用户列表-->
<!-- 当我们根据过滤条件查询时,如果需要用到模糊查询,我们不能用占位符#{},
得用${}拼接将查询的sql和参数拼接起来
注意:拼接符容易引起sql注入和sql执行效率问题,慎用!
能用占位符就不用拼接符
-->
<select id="getUserListByUserDto" parameterType="com.steven.mybatis.sysmanage.dto.UserDto" resultType="user">
SELECT USER_ID,USER_NAME,LOGIN_NAME,PASSWORD,DEPT_ID,BIRTHDAY,TV_UPDATE FROM M_USER
<!-- where标签有两个用途:1:添加sql的where关键字;2:判断第一个条件不需要and -->
<where>
<if test="user!=null">
<if test="user.loginName!=null">AND LOGIN_NAME LIKE '%${user.loginName}%'</if>
<if test="user.userName!=null">AND USER_NAME LIKE '%${user.userName}%'</if>
<if test="user.birthday!=null">AND BIRTHDAY=#{user.birthday}</if>
<if test="user.deptId!=null">AND DEPT_ID=#{user.deptId}</if>
</if>
</where>
</select>
<!-- 查询用户数目 -->
<select id="getUserCount" resultType="int">
SELECT COUNT(0) FROM M_USER
</select>
<!-- 查询所有用户信息 -->
<select id="getUserList" resultType="user">
SELECT USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE FROM M_USER
</select>
<!-- 查询所有用户信息,用map返回-resultType -->
<select id="getUserListMap" resultType="hashMap">
SELECT USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE FROM M_USER
</select>
<!-- 定义一个resultMap id="userResultMap" -->
<resultMap type="user" id="userResultMap">
<!-- id标签代表数据库的主键;column代表列名或者sql中的别名;property代表java对象的属性名 -->
<!-- 当数据库字段名和java类属性名遵循了命名规则时,下面的对应关系可以省略不用写 -->
<id column="ID" property="userId"></id>
<result column="USER_NAME" property="userName"></result>
<result column="LOGIN_NAME" property="loginName"></result>
<result column="BIRTHDAY" property="birthday"></result>
<result column="TV_UPDATE" property="tvUpdate"></result>
</resultMap>
<!-- 查询所有用户信息,以resultMap方式返回 -->
<select id="getUserListByResultMap" resultMap="userResultMap">
SELECT USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE FROM M_USER
</select>
<!-- 增加用户记录 -->
<insert id="addUser" parameterType="user">
INSERT INTO M_USER(USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE)
VALUES(#{userName},#{loginName},#{birthday},#{tvUpdate})
</insert>
<!-- 删除用户 -->
<delete id="delUser" parameterType="user">
DELETE FROM M_USER WHERE USER_ID=#{userId}
</delete>
<!-- 修改用户 -->
<update id="updateUser" parameterType="user">
UPDATE M_USER SET USER_NAME=#{userName},LOGIN_NAME=#{loginName},
BIRTHDAY=#{birthday} WHERE USER_ID=#{userId}
</update>
</mapper>
9.4.5 测试
package com.steven.mybatis.sysmanage.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
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 org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import com.steven.mybatis.sysmanage.dto.UserDto;
import com.steven.mybatis.sysmanage.entity.User;
import com.steven.mybatis.sysmanage.mapper.UserMapper;
public class MyBatisMapperTest {
static Logger log = Logger.getLogger(MyBatisMapperTest.class);
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void init() throws IOException{
String configFile = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(configFile);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
//根据用户id查询用户,明细信息
@Test
public void testGetUserById(){
//在用mapper代理方式进行开发的时候,通过sqlSession.getMapper获取接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
log.info(userMapper.getUserById(1L));
}
//根据用户名和密码验证用户是否存在
@Test
public void testGetUserByLoginNameAndPsw(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
log.info(userMapper.getUserByLoginNameAndPsd("lilei", "abc"));
}
//根据UserDto对象验证用户是否存在
@Test
public void testGetUserListByUserDto(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserDto userDto = new UserDto();
// User user = new User();
// user.setLoginName("Steven");
// userDto.setUser(user); 如果不设置属性,就返回全部
log.info(userMapper.getUserListByUserDto(userDto));
}
//查询用户总数
@Test
public void testGetUserCount(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
log.info(userMapper.getUserCount());
}
//查询所有用户:
//方式一:测试返回结果为User的对象格式
@Test
public void testGetUserList(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
log.info(userList);
}
//方式二:测试返回结果为hashmap的对象格式
@Test
public void testGetUserListMap(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<Map<Object,Object>> userListMap = userMapper.getUserListMap();
log.info(userListMap);
}
//方式三:测试resultMap的返回结果
@Test
public void testGetUserListByResultMap(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserListByResultMap();
log.info(userList);
}
//增、删、改省略
}
查询所有用户三种方式结果对比:
方式一:getUserList
[User [userId=1, userName=chenyang, loginName=Steven, password=null, deptId=null, birthday=1986-09-18 00:00:00.0, tvUpdate=2018-02-28 00:00:00.0], User [userId=2, userName=lilei, loginName=lilei, password=null, deptId=null, birthday=1989-09-10 00:00:00.0, tvUpdate=2018-02-28 00:00:00.0], User [userId=4, userName=zhangsan, loginName=Jerry, password=null, deptId=null, birthday=1990-10-03 00:00:00.0, tvUpdate=2018-02-28 00:00:00.0]]
方式二:getUserListMap(它是根据sql语句中要select的字段来显示,sql中没有PASSWORD和DEPT_ID,所有结果中也没有这两个结果)
[{USER_ID=1, TV_UPDATE=2018-02-28 00:00:00.0, USER_NAME=chenyang, LOGIN_NAME=Steven, BIRTHDAY=1986-09-18 00:00:00.0}, {USER_ID=2, TV_UPDATE=2018-02-28 00:00:00.0, USER_NAME=lilei, LOGIN_NAME=lilei, BIRTHDAY=1989-09-10 00:00:00.0}, {USER_ID=4, TV_UPDATE=2018-02-28 00:00:00.0, USER_NAME=zhangsan, LOGIN_NAME=Jerry, BIRTHDAY=1990-10-03 00:00:00.0}]
方式三:getUserListByResultMap(第一种方式与第三种方式返回结果一致)
[User [userId=1, userName=chenyang, loginName=Steven, password=null, deptId=null, birthday=1986-09-18 00:00:00.0, tvUpdate=2018-02-28 00:00:00.0], User [userId=2, userName=lilei, loginName=lilei, password=null, deptId=null, birthday=1989-09-10 00:00:00.0, tvUpdate=2018-02-28 00:00:00.0], User [userId=4, userName=zhangsan, loginName=Jerry, password=null, deptId=null, birthday=1990-10-03 00:00:00.0, tvUpdate=2018-02-28 00:00:00.0]]
9.5 动态sql
- if标签和where标签
例见9.4. - foreach
需求:需要查询指定id集合的所有用户信息
SELECT * FROM M_USER WHERE USER_ID IN (1,2,3)
- sql片段
将通用的sql语句提取出来,称为sql片段,给不同方法应用 - 增加记录返回主键
<!--以oracle数据库为例,需要调用序列 -->
<!-- <selectKey keyProperty="userId" resultType="long" order="BEFORE">
SELECT IDSEQUENCE.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO M_USER(USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE)
VALUES(#{userId},#{userName},#{loginName},#{birthday},#{tvUpdate}) -->
<!-- 以mysql为例 -->
<selectKey keyProperty="userId" resultType="long" order="AFTER">
SELECT LAST_INSERT_ID() AS userId
</selectKey>
INSERT INTO M_USER(USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE)
VALUES(#{userName},#{loginName},#{birthday},#{tvUpdate})
- 在UserDto包装类中增加ids属性
package com.steven.mybatis.sysmanage.dto;
import java.util.List;
import com.steven.mybatis.sysmanage.entity.User;
/**
* 跟数据库M_USER表对应的实体类
* @author chenyang
*
*/
public class UserDto implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private User user;
private List<Long> ids;
public User getUser(){
return user;
}
public void setUser(User user){
this.user = user;
}
public List<Long> getIds() {
return ids;
}
public void setIds(List<Long> ids) {
this.ids = ids;
}
}
- mapper文件
<?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="com.steven.mybatis.sysmanage.mapper.UserMapper">
<select id="getUserListByUserDto" parameterType="com.steven.mybatis.sysmanage.dto.UserDto" resultType="user">
SELECT
<include refid="user_column"></include>
FROM M_USER
<!-- where标签有两个用途:1:添加sql的where关键字;2:判断第一个条件不需要and -->
<where>
<include refid="query_user_sql_where"></include>
</where>
</select>
<!--将通用的sql语句提取出来,称为sql片段,给不同方法复用 -->
<sql id="query_user_sql_where">
<if test="user!=null">
<if test="user.loginName!=null">AND LOGIN_NAME LIKE '%${user.loginName}%'</if>
<if test="user.userName!=null">AND USER_NAME LIKE '%${user.userName}%'</if>
<if test="user.birthday!=null">AND BIRTHDAY=#{user.birthday}</if>
<if test="user.deptId!=null">AND DEPT_ID=#{user.deptId}</if>
</if>
<!-- 需求:实现sql的where部分: AND USER_ID IN(1,2,3) -->
<if test="ids!=null">
<!-- collection:输入参数的属性;open:循环前的开始符合;close:循环后的结束符合 -->
<foreach collection="ids" open="AND USER_ID IN(" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</sql>
<!-- slq语句中的列名也可以复用 -->
<sql id="user_column">USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE</sql>
<sql id="user_column_with_alisas">A.USER_ID,A.USER_NAME,A.LOGIN_NAME,A.BIRTHDAY,A.TV_UPDATE</sql>
<insert id="addUser" parameterType="user">
<!-- 以mysql为例,由于mysql中可以用到自增长,USER_ID是自增长,需求:返回userId值 -->
<selectKey keyProperty="userId" resultType="long" order="AFTER">
SELECT LAST_INSERT_ID() AS userId
</selectKey>
INSERT INTO M_USER(USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE)
VALUES(#{userName},#{loginName},#{birthday},#{tvUpdate})
<!--以oracle数据库为例,没有自增长,需要调用序列 -->
<!-- <selectKey keyProperty="userId" resultType="long" order="BEFORE">
SELECT IDSEQUENCE.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO M_USER(USER_ID,USER_NAME,LOGIN_NAME,BIRTHDAY,TV_UPDATE)
VALUES(#{userId},#{userName},#{loginName},#{birthday},#{tvUpdate}) -->
</insert>
</mapper>
- 测试
package com.steven.mybatis.sysmanage.test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
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 org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import com.steven.mybatis.sysmanage.dto.UserDto;
import com.steven.mybatis.sysmanage.entity.User;
import com.steven.mybatis.sysmanage.mapper.UserMapper;
public class MyBatisMapperTest {
static Logger log = Logger.getLogger(MyBatisMapperTest.class);
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void init() throws IOException{
String configFile = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(configFile);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
//根据UserDto对象验证用户是否存在
@Test
public void testGetUserListByUserDto(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserDto userDto = new UserDto();
// User user = new User();
// user.setLoginName("Steven");
// userDto.setUser(user);
List<Long> ids = new ArrayList<Long>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
userDto.setIds(ids);
log.info(userMapper.getUserListByUserDto(userDto));
}
@Test
public void testAddUser() throws IOException{
User user = new User();
user.setUserName("wangwu");
user.setLoginName("ww");
user.setBirthday(new Timestamp(System.currentTimeMillis()));
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.addUser(user);
//测试通过mapper配置文件中的selectKey标签返回userId
log.info(user.getUserId());
//注意:增删改操作的时候,需要提交事务 sqlSession.commit();
sqlSession.commit();
sqlSession.close();
}
}
God will send the rain when you are ready.You need to prepare your field to receive it.