目录
MyBatis文档
MyBatis源码及资料: https://github.com/coderZYGui/MyBatis-Study
MyBatis系列
一、MyBatis的CRUD操作
- 编写接口
public interface UserMapper {
// 查询所有用户
List<User> getUserList();
// 根据id来查询用户
User getUser(int id);
// 根据id来更新用户信息
int updateUser(User user);
// 插入一条用户信息
int insertUser(User user);
// 根据id来删除用户
int deleteUser(int id);
}
- 编写mybatis映射文件/Mapper文件
select元素
: 专门用来做查询的SQLid属性
: 唯一标识, 对应mapper接口中的接口方法, 和接口方法完全一致parameterType属性
: 表示执行该SQL语句需要的参数的类型(建议不写),MyBatis可以自行推断出来resultType属性
: 把结果集中每一行数据封装成什么类型的对象resultMap属性
: 当表字段和对象属性名不同, 需要使用resultMap进行映射; (和resultType不能同时使用)- 其他属性:
获取自增主键的值
- 获取
自动增长
的主键 (MySQL)
insert标签
: 插入的sqluseGeneratedKeys属性
: 是否需要返回自动生成的主键keyProperty属性
: 把自动生成的主键值设置到对象的哪一个属性上, 一般为id
<?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">
<!--命名空间,类似包的概念: namespace:绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.sunny.dao.UserMapper">
<!--
select元素: 专门用来做查询的SQL
-id属性: 唯一标识,用来标识某一条SQL语句
-parameterType属性: 表示执行该SQL语句需要的参数的类型(建议不写),MyBatis可以自行推断出来
-resultType属性: 把结果集中每一行数据封装成什么类型的对象
-->
<select id="getUserList" resultType="com.sunny.domain.User">
SELECT * FROM user;
</select>
<!-- #{id} 相当于在测试方法中selectOne传递过来的值 -->
<select id="getUser" parameterType="int" resultType="com.sunny.domain.User">
SELECT * FROM user WHERE id = #{id};
</select>
<update id="updateUser" parameterType="com.sunny.domain.User">
UPDATE user SET name = #{name}, pwd = #{pwd} WHERE id = #{id};
</update>
<!--
这里的parameterType可以不写
insert元素:
获取自动增长的主键
useGeneratedKeys属性: 是否需要返回自动生成的主键
keyProperty: 把自动生成的主键值设置到对象的哪一个属性上
-->
<insert id="insertUser" parameterType="com.sunny.domain.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (id, name, pwd) VALUES (null , #{name}, #{pwd});
</insert>
<delete id="deleteUser" parameterType="int">
DELETE FROM user WHERE id = #{id};
</delete>
</mapper>
- UserMapperTest
执行SQL:
原理
- UserMapper.xml中的
namespace
:绑定一个对应的Dao/Mapper接口
相当于UserMapper接口的实现类, 因为通过namespace反射+动态代理获取该类的对象- 这里用UserMapper.class,就是面向接口编程,相当于获取UserMapper的实现类,然后通过实现类来调用接口方法
public class UserMapperTest {
/**
* 查询所有用户
*
* @throws IOException
*/
@Test
public void testQueryUserList() throws IOException {
//1. 获得sqlSession对象
// SqlSession sqlSession = MybatisUtils.getSqlSession();
//1. 从classpath路径去加载MyBatis全局配置文件:mybatis-config.xml
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2. 创建sqlSessionFactory对象,好比是DataSource
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3. 创建sqlSession对象,好比是Connection
SqlSession sqlSession = factory.openSession();
//4. 具体操作
// 执行SQL(方式一)
/*
执行SQL: UserMapper.xml中的namespace:绑定一个对应的Dao/Mapper接口相当于UserMapper接口的实现类,
这里用UserMapper.class,就是面向接口编程,相当于获取UserMapper的实现类,然后通过实现类来调用接口方法
---> 底层实际是使用反射的方式了, 创建UserMapper的实现类
*/
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
// 方式二
// List<User> userList = sqlSession.selectList("com.sunny.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
// 关闭sqlSession
sqlSession.close();
}
/**
* 查询id为1的用户
*
* @throws IOException
*/
@Test
public void testQueryOneUser() throws IOException {
// 加载全局配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 构建sqlSessionFactory工厂类对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 根据sqlSession类对象来创建SqlSession对象
SqlSession sqlSession = factory.openSession();
// sqlSession相当于Connection,来执行SQL语句
//User user = sqlSession.selectOne("com.sunny.dao.UserMapper.getUser", 1L);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUser(1);
System.out.println(user);
sqlSession.close();
}
/**
* 修改一条语句的内容
*
* @throws Exception
*/
@Test
public void testUpdateUser() throws Exception {
User user = new User();
user.setId(4);
user.setName("guizy");
user.setPwd("10004");
// 获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 方式一
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int update = mapper.updateUser(user);
// 方式二(以前的,不推荐写)
// int update = sqlSession.update("com.sunny.dao.UserMapper.update",user);
if (update > 0) {
System.out.println("成功修改了:" + update + " 条语句!");
}
// 增删改必须提交事务
sqlSession.commit();
// 关闭资源
sqlSession.close();
}
/**
* 插入一条数据
* @throws Exception
*/
@Test
public void testInsertUser() throws Exception {
// 获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
/*
执行SQL: UserMapper.xml中的namespace:绑定一个对应的Dao/Mapper接口相当于UserMapper接口的实现类,
这里用UserMapper.class,就是面向接口编程,相当于获取UserMapper的实现类,然后通过实现类来调用接口方法
*/
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User u = new User("coder", "10007");
int count = mapper.insertUser(u);
if (count > 0){
System.out.println("插入成功!");
}
// 提交事务
sqlSession.commit();
// 关闭资源
sqlSession.close();
System.out.println(u);
}
/**
* 根据id来删除用户
* @throws Exception
*/
@Test
public void testDeleteUser() throws Exception{
// 加载mybatis全局配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 构建SqlSessionFactory工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 根据工厂对象来创建SqlSession对象
// 这样的话,下面就不用手动提交事务了,设置了自动提交事务
SqlSession sqlSession = factory.openSession(true);
// SqlSession sqlSession = factory.openSession();
// 根据sqlSession对象来获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 通过mapper来调用接口方法
int count = mapper.deleteUser(5);
if (count > 0){
System.out.println("删除成功");
}
// 提交事务
// sqlSession.commit();
// 关闭资源
sqlSession.close();
}
}
注意: 增删改操作,必须要手动提交事务,否则对该操作不起作用!
SqlSession sqlSession = factory.openSession(true); // 就不用手动提交事务了
二、MyBatis拓展
1、别名配置处理
- Mapper.xml中的查询操作
<select id="getUserList" resultType="com.sunny.domain.User">
SELECT * FROM user;
</select>
<!-- #{id} 相当于在测试方法中selectOne传递过来的值; 底层调用User类的getId方法 -->
<select id="getUser" resultType="com.sunny.domain.User">
SELECT * FROM user WHERE id = #{id};
</select>
注意: 当写 resultType
结果类型时,都使用全限定类名com.sunny.domain.User
,很麻烦,可以使用类型别名
- 使用别名(
typeAlias
)是因为类全限定名太长,不方便.MyBatis中别名不区分大小写
1.2、 自定义别名
- 在全局配置文件mybatis-config.xml中定义别名
- 直接给某一个类起别名
<typeAlias type="com.sunny.domain.User" alias="User" />
给一个包(包含子包)中所有的类起别名 (一般使用这种方式)
一般写到domain包就可以了,自动为该包中的类名起别名,<package name="com.sunny.domain"/>
默认的别名就是类名首字母小写(不区分大小写)
@Alias注解
可以用于设置类的别名(优先级最高)
<!--类型别名-->
<!--
在实体类比较少的时候,使用第一种方式。
如果实体类十分多,建议使用第二种方式。
-->
<typeAliases>
<!--<typeAlias type="com.sunny.domain.User" alias="User" />-->
<!--
一般写到domain包就可以了,自动为该包中的类名起别名,默认的别名就是类名首字母小写(不区分大小写)
-->
<package name="com.sunny.domain"/>
</typeAliases>
前两种设置别名的使用:
- 在实体类比较少的时候,使用第一种方式。
如果实体类十分多,建议使用第二种方式。
- Mapper.xml中的查询操作(使用别名)
<select id="getUserList" resultType="User">
SELECT * FROM user;
</select>
<!-- #{id} 相当于在测试方法中selectOne传递过来的值 -->
<select id="getUser" resultType="User">
SELECT * FROM user WHERE id = #{id};
</select>
注意: 在全局配置文件中<configuration>
标签中属性设置,日志设置,别名设置.都有严格的顺序,不能打乱.
1.3、只返回表中指定列
- UserMapper.java
public interface UserMapper {
// 只查询User表的id和name列
List<User> queryUserByIdAndName();
List<Map<Integer, Object>> queryUserByIdAndName2();
}
- UserMapperTest
public class UserMapperTest {
/**
* 只查询User表中的id和name列
* @throws Exception
*/
@Test
public void testQueryUserByIdAndName(){
/*
这种方式来查询User表中的部分列,其他列会显示null,可以将
*/
/*SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.queryUserByIdAndName();
for (User user : users) {
System.out.println(user);
}*/
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Map<Integer, Object>> maps = mapper.queryUserByIdAndName2();
for (Map<Integer, Object> map : maps) {
System.out.println(map);
}
}
}
- UserMapper.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">
<!--命名空间,类似包的概念: namespace:绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.sunny.dao.UserMapper">
<!--只查询user表的id和name列-->
<!-- <select id="queryUserByIdAndName" resultType="User">-->
<!-- SELECT id,name FROM user;-->
<!-- </select>-->
<select id="queryUserByIdAndName2" resultType="map">
SELECT id, name FROM user;
</select>
</mapper>
1.4、 系统自带的别名
跳转到目录
一般用于接口方法返回值类型
, 当接口方法返回为为int时, 可以在Mapper.xml文件中ResultType设置为int(默认), _int(别名)
,对大小写不敏感.
2、 属性配置处理
property元素
- 修改mybatis-config.xml文件, 新增
<!--属性配置-->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="1111"/>
</properties>
- 数据库配置中,使用
$表达式
获取属性值
<!--连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
db.properties文件
- 新增
db.properties
文件:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=1111
- 修改mybatis-config.xml文件:
<!--属性配置-->
<properties resource="db.properties"/>
3、 查询结果映射
- 在CRUD中查询方法直接通过设置别名的方式
resultType="User"
来设置MyBatis把结果集中的每一行数据包装为User对象
,此时必须要求数据表中的列名和对象的属性名一一对应
. - 如果表中的列名和对象属性名不匹配,此时的解决方案是?
3.1、 使用给字段起别名的方式
- 将关系表中的列名设置和对象中属性同名
<select id="queryUserList" resultMap="BaseResultMap"> SELECT u_id AS id, u_name AS name, u_pwd AS pwd FROM user1; </select>
mybatis-config.xml中的setting设置
开启驼峰映射 (数据库字段和实体属性符合驼峰转换规则), 开启该设置后, 会自动映射成功
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.2、 resultMap元素
- 待解决问题: 结果集(表)中的列名和对象中的字段名称不匹配
- 解决方案: 使用
resultMap
元素
resultMap元素: 结果集对象的映射
id属性: 当前Mapper文件中resultMap的唯一名称
type属性: 把结果集中的每一行数据封装成什么类型的对象
子元素:
id元素: 功能和result一样,如果是主键,建议使用id元素,提升性能
result元素: 匹配对象中的哪一个属性对应表中的哪一个列
通过这种数据库表字段
和实体类属性
的映射
就可以解决他们之间的不匹配
问题, 我们之间查询数据库字段
名即可, 不需要再为字段起别名了
<!--命名空间,类似包的概念: namespace:绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.sunny.dao.UserMapper">
<!--查询一共有多少用户-->
<select id="queryUserCount" resultType="int">
SELECT COUNT(id) FROM user1;
</select>
<resultMap id="BaseResultMap" type="User">
<result column="u_id" property="id" />
<result column="u_name" property="name" />
<result column="u_pwd" property="pwd" />
</resultMap>
<!-- 此处就使用 结果集对象映射, BaseResultMap -->
<select id="queryUserList" resultMap="BaseResultMap">
select u_id, u_name, u_pwd from user1;
</select>
</mapper>
4、 Mapper组件
Mapper组件: Mapper接口 + Mapper.xml文件
下面有两种方式通过sqlSession
来获取SQL
并执行,下面说说两种方式的优缺点:
/**
* 查询id为1的用户
*
* @throws IOException
*/
@Test
public void testQueryOneUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 使用方式一: 来找到SQL并执行, arg1:定义的Mapper接口 arg2: 给该接口传递的参数
//User user = sqlSession.selectOne("com.sunny.dao.UserMapper.getUser", 1L);
// 使用方式二: 来找到SQL并执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 实际上底层调用的还是sqlSession中selectXxx的方法,注意:sqlSession调用CRUD方法只能传递一个参数
User user = mapper.getUser(1);
System.out.println(user);
sqlSession.close();
}
- 使用
namespace.id
(方式一)的方式去找到SQL元素的方式,会有几个问题:
namespace.id
使用的是String类型,一旦编写错误,只有等到运行时
才能报错。- 传入的实际参数类型不能被检查。
- 每一个操作的代码模板类似。
这种方式相对于方式二,我觉得唯一的优点就是在接口中不需要定义接口方法,直接和映射文件关联起来了
.
解决方案:
使用Mapper接口,类似DAO接口,在Mapper接口中定义每一个操作方法.(上面已经使用过,这里总结一下)
4.1、 Mapper接口
使用Mapper组件:
- 创建一个
Mapper接口(类似于DAO接口)
, 接口的要求:
①这个接口的全限定名称
对应着 Mapper文件的namespace
;
②这个接口中的方法
和Mapper文件中的SQL元素一一对应
;
- 接口方法的
名字
对应SQL元素的id
;- 接口方法的
参数类型
对应SQL元素中定义的paramterType
类型( 一般不写) ;- 接口方法的
返回类型
对应SQL元素中定义的resultType/resultMap(结果集对象映射:表列名和对象字段名不匹配时使用)
类型;
- 创建SqlSession ;
- 通过SqlSession.getMapper(XxxMapper.class)方法得到一个XxxMapper的实现类对象;
- 调用Mapper对象上的方法完成对象的CURD ;
SqlSession.getMapper(XxxMapper.class)
: 的底层原理
- 执行SQL: UserMapper.xml中的namespace:绑定一个对应的Dao/Mapper接口相当于UserMapper接口的实现类,
- 这里用UserMapper.class,就是面向接口编程,相当于获取UserMapper的实现类,然后通过实现类来调用接口方法
- 底层实际是使用
反射 + 动态代理
的方式了, 创建UserMapper的实现类
5、 参数处理
跳转到目录
注意: 在SqlSession中的CRUD方法
最多只能传递一个参数
. 即使上面的方式二,底层仍是使用SqlSession来调用CRUD方法,如何来解决CRUD方法中只能传递一个参数呢?
-
方式一: 将参数封装到一个
JavaBean
中,将JavaBean对象作为参数传递 -
方式二: 使用
Map
来封装参数_键值对的方式 -
方式三: 使用注解
@Param
Param注解中的字符串就是Map中的key
-
ClientMapper.java
public interface ClientMapper {
// 方式一: 把多个参数封装成JavaBean
Client login1(LoginVO vo);
// 方式二: 使用Map对象封装多个参数
Client login2(Map<String, Object> paramMap);
// 方式三: 使用Param注解, 原理是方式二. Param注解中的字符串就是Map中的key
Client login3(@Param("username") String username, @Param("password") String password);
}
- ClientMapper.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">
<!--命名空间,类似包的概念: namespace:绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.sunny.dao.ClientMapper">
<select id="login1" parameterType="LoginVo" resultType="Client">
SELECT id, username, password FROM client WHERE username = #{username} AND password = #{password};
</select>
<select id="login2" parameterType="map" resultType="Client">
SELECT id, username, password FROM client WHERE username = #{username} AND password = #{password};
</select>
<select id="login3" resultType="Client">
SELECT id, username, password FROM client WHERE username = #{username} AND password = #{password};
</select>
</mapper>
- ClientMapperTest.java
public class ClientMapperTest {
/**
* 方式一: 使用JavaBean来封装
*/
@Test
public void testLogin1(){
LoginVO vo = new LoginVO("zy", "1111");
SqlSession sqlSession = MybatisUtils.getSqlSession();
ClientMapper mapper = sqlSession.getMapper(ClientMapper.class);
// mapper调用方法,实际底层仍是sqlSession来调用select方法
Client client = mapper.login1(vo);
System.out.println(client);
sqlSession.close();
}
/**
* 使用Map来封装
*/
@Test
public void testLogin2(){
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("username", "zy");
paramMap.put("password", "1111");
SqlSession sqlSession = MybatisUtils.getSqlSession();
ClientMapper mapper = sqlSession.getMapper(ClientMapper.class);
// mapper调用方法,实际底层仍是sqlSession来调用select方法.
// 注意: sqlSession调用的方法,只能传递一个参数.
Client client = mapper.login2(paramMap);
System.out.println(client);
sqlSession.close();
}
/**
* 方式三: 使用Param注解,原理是方式二
*/
/*
HelloWorld
*/
@Test
public void testLogin3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
ClientMapper mapper = sqlSession.getMapper(ClientMapper.class);
// mapper调用方法,实际底层仍是sqlSession来调用select方法.
// 注意: sqlSession调用的方法,只能传递一个参数.
Client client = mapper.login3("zy", "1111");
System.out.println(client);
sqlSession.close();
}
}
5.1、OGNL介绍
对象导航图语言
(Object Graph Navigation Language),简称OGNL
,是应用于Java中的一个开源的表达式语言(Expression Language),它被集成在Struts2等框架中。- 作用 : 对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能。
ONGL可以存取对象的属性和调用对象的方法, 通过OGNL的表达式迭代出整个对象的结构图。
- OGNL语法
#{}
- 如果上下文 (resultType)的对象是JavaBean对象employee, 就可以直接通过
#{}
来获取该对象的属性值;获取该属性的get方法
#{name}
: 相当于获取employee的getName()
方法#{age}
: 相当于获取employee的getAge()
方法#{dept.name}
: 相当于获取employee的getDept().getName()
方法
- 如果上下文对象是Map对象,
#{key}
6、 # 和 $ 的区别
-
使用
#
Mapper.xml: SELECT id, username, password FROM client WHERE username = #{username} AND password = #{password} -- 上面的代码会预编译为下面这样 Preparing: SELECT id, username, password FROM client WHERE username = ? AND password = ?; Parameters: zy(String), 1111(String)
-
使用
$
Mapper.xml: SELECT id, username, password FROM client WHERE username = ${username} AND password = ${password} -- 上面的代码直接会转为下面的, 不会进行预编译为?, 可能造成sql注入 Preparing: SELECT id, username, password FROM client WHERE username = root AND password = 1111; Parameters:
一、两者的异同
-
相同: 都可以通过
#
和$
来获取对象中的信息
-
不同:
- 使用
#
传递的参数会先预编译转换为占位符?
, 通过设置占位符参数的方式来设置值(会给值使用单引号
引起来) - 使用
$
传递的参数,直接把解析出来的数据作为SQL语句的一部分
- 使用
二、推论
- #: 好比使用
PrepareStatement
,不会导致SQL注入问题
,相对安全 - $: 好比使用
Statement
,可能会导致SQL注入问题
,相对不安全.
三、如何选择
- 如果需要设置
占位符参数
的地方,全部使用 # ,也就是SQL之后可能使用 ? 的情况 - 如果我们传递过去的对象字段的内容应该作为SQL的一部分, 此时使用 $ , 比如
排序、分组
查询等- 原生jdbc不支持占位符的地方我们就可以使用${}进行取值
- 比如分表、排序;按照年份分表拆分
- select * from ${year}_salary where xxx;
- select * from tbl_employee order by ${f_name} ${order}
Mybatis参数处理总结:
-
单个参数
单个参数(没有标注@param注解):mybatis不会做特殊处理,底层不会封装map,
直接 #{参数名/任意名}:取出参数值
。 -
多个参数
多个参数:mybatis会做特殊处理。 多个参数会被封装成 一个map, key:param1...paramN,或者参数的索引也可以 value:传入的参数值 #{}就是从map中获取指定的key的值; 异常: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2] 操作: 方法:public Employee getEmpByIdAndLastName(Integer id,String lastName); 取值:#{id},#{lastName} ; 会报上面的异常 解决: #{param1}, #{param2} 这样太麻烦了, 参数一多容易混乱, 参考下面的: 命名参数 解决方式: 【命名参数】:明确指定封装参数时map的key;@Param("id") 多个参数会被封装成 一个map, key:使用@Param注解指定的值 value:参数值 #{指定的key}取出对应的参数值 此时: #{id}, #{lastName}就不会报错了 举例: public Employee getEmp(@Param("id")Integer id, String lastName); 取值:id==>#{id/param1} lastName==>#{param2} public Employee getEmp(Integer id, @Param("e")Employee emp); 取值:id==>#{param1} lastName===>#{param2.lastName/e.lastName} public Employee getEmpById(List<Integer> ids); 取值:取出第一个id的值: #{list[0]}, 一般使用动态sql对ids进行遍历 public Employee getEmpById(Integer[] ids); 取值:取出第一个id的值: #{array[0]}, 一般使用动态sql对ids进行遍历
-
参数封装为POJO
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值 -
参数封装为Map
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key}:取出map中对应的value
拓展: #{ }的一些特殊用法
#{}:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
eg:
#{createTime,jdbcType=TIMESTAMP} ; 跟上createTime在数据库中的类型
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
1、#{email, jdbcType=NULL};
2、jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>
三、MyBatis使用注解开发
- 之前的开发MyBatis都使用
XML
来配置,也可以直接用注解
来开发,当SQL很复杂的时候,推荐使用XML
来配置
注意: 使用注解来配置的话, 就不需要Mapper.xml文件了,在主配置文件中注册映射文件的方式就要改为:
<mapper class="com.sunny.mapper.UserMapper" />
或者
<mapper package="com.sunny.mapper"/>
该包下的mapper都注册
在接口方法上添加注解
- Insert注解
跳转到目录
// 插入一条用户信息
@Insert("INSERT INTO user (id, name, pwd) VALUES (null , #{name}, #{pwd})")
@Options(useGeneratedKeys = true, keyProperty = "id") //生成主键
int insertUser(User user);
- Delete注解
跳转到目录
// 根据id来删除用户
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteUser(int id);
- Update注解
跳转到目录
// 根据id来更新用户信息
@Update("UPDATE user SET name = #{name}, pwd = #{pwd} WHERE id = #{id}")
int updateUser(User user);
- Select注解
跳转到目录
// 查询某个用户
@Select("SELECT id AS u_id, name AS u_name, pwd AS u_pwd FROM user WHERE id = ${id}")
@ResultMap("BaseResultMap")
User getOneUser(int id);
// 查询所有用户
@Select("SELECT id AS u_id, name AS u_name, pwd AS u_pwd FROM user")
@Results(id = "BaseResultMap", value = {
@Result(column = "u_id", property = "id"),
@Result(column = "u_name", property = "name"),
@Result(column = "u_pwd", property = "pwd")
})
List<User> getUserList();
2021.4.19查漏补缺
databaseIdProvider标签
用来支持多数据库厂商,在全局配置文件中添加后为各数据库起别名,
在mapper.xml中标签中添加databaseId属性,即可指定该sql语句适用的数据库。
然后修改的default值可以切换运行环境
<!--1.配置数据库的环境-->
//这里使用的是mysql数据源
<environments default="dev_mysql">
<!--开发环境:在以后事务管理器和连接池都是交给Spring框架来管理-->
<environment id="dev_mysql">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="1111"/>
</dataSource>
</environment>
<environment id="dev_oracle">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--连接池-->
<dataSource type="POOLED">
<!-- 这里配置oracle的信息 -->
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR>
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
<mapper标签的细节
<?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>
<!--
1、mybatis可以使用properties来引入外部properties配置文件的内容;
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
-->
<properties resource="dbconfig.properties"></properties>
<!--
2、settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 3、typeAliases:别名处理器:可以为我们的java类型起别名
别名不区分大小写
-->
<typeAliases>
<!-- 1、typeAlias:为某个java类型起别名
type:指定要起别名的类型全类名;默认别名就是类名小写;employee
alias:指定新的别名
-->
<!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> -->
<!-- 2、package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
-->
<package name="com.atguigu.mybatis.bean"/>
<!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
</typeAliases>
<!--
4、environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
transactionManager:事务管理器;
type:事务管理器的类型;JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
自定义事务管理器:实现TransactionFactory接口.type指定为全类名
dataSource:数据源;
type:数据源类型;UNPOOLED(UnpooledDataSourceFactory)
|POOLED(PooledDataSourceFactory)
|JNDI(JndiDataSourceFactory)
自定义数据源:实现DataSourceFactory接口,type是全类名
-->
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<environment id="dev_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
<!-- 5、databaseIdProvider:支持多数据库厂商的;
type="DB_VENDOR":VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
MySQL,Oracle,SQL Server,xxxx
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<!-- 6、mappers:将sql映射注册到全局配置中 -->
<mappers>
<!--
mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml
注册接口
class:引用(注册)接口,
1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
所以为了mapper.xml的归类, 所以创建了和dao同名的目录,
在编译运行后会合并在同一目录下
2、没有sql映射文件,所有的sql都是利用注解写在接口上;
推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件
不重要,简单的Dao接口为了开发快速可以使用注解;
-->
<!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
<!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
<!-- 批量注册: -->
<package name="com.atguigu.mybatis.dao"/>
</mappers>
</configuration>
拓展: 获取非自动增长的主键
- 获取
非自动增长
的主键- 在Oracle数据库中不支持自增主键, 按照序列自增的
selectKey
中resultType属性指定期望主键的返回的数据类型,
keyProperty
属性指定实体类对象接收该主键的字段名
order
属性指定执行查询主键值SQL语句是在插入语句执行之前还是之后(可取值:after和before, 一般用before)
<insert id="insertDataAgain">
<selectKey resultType="integer" keyProperty="id" order="before">
<!-- SELECT LAST_INSERT_ID() -->
<!-- 表已经添加了自增序列 -->
SELECT 表的序列.nextval from dual //因为是before, 先执行select查到自增序列是多
少, 然后将序列值交给keyProperty的id, 然后插入的时候再取id值
</selectKey>
INSERT INTO tableName VALUES(#{id}, #{name})
</insert>