SQL映射文件
- SQL映射文件
- 使用MyBatis实现条件查询
- select
- 单个参数select
- 对象(实体类)参数select
- 使用@Param注解实现多个参数传入
- 使用Map参入
- 使用resultMap完成查询
- resultType和resultMap的关联和区别?
- MyBatis实现增删改
- collection
- resultMap自动映射级别和MyBatis缓存
- resultMap自动映射级别
- MyBatis缓存
SQL映射文件
作用:可以通过读取映射文件调用相应的方法,执行SQL操作;sql语句都写在映射文件中(查询,插入,删除,更新等)
首先了解映射文件的几个顶级元素配置:
- mapper:映射文件的跟元素节点,只有一个属性namespace(命名空间 )作用:
(1)区分不同的mapper,全局唯一
(2)绑定DAO接口,及面向接口编程。当namespce绑定某一接口之后,可以不用写该接口的实现类,MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句,因此namespace的命名必须要跟接口同名。 - cache:给定命名空间的缓存
- cache-ref:从其它命名空间引用缓存配置。
- resultMap:用来描述数据库结果集和对象的对应关系。
- sql:可以重用的SQL块,可以被其他Sql语句引用
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- select – 映射注意查询语句
注意
(1)namespace命名必须跟某个DAO接口同名同属DAO层,所以代码结构上,映射文件与DAO接口放在同包下,并且XML习惯以mapper结尾。
(2)在不同的mapper文件中,子元素的id可以相同,MyBatis通过namespace和子元素的id联合区分,接口中的方法与映射文件中的SQL语句一 一对应。
使用MyBatis实现条件查询
select
select语句有很多属性可以详细配置每一条语句
- id:命名空间中唯一的标识符,接口中的方法与映射文件中的SQL语句id一一对应
- parameterType:传入SQL语句的参数类型
- resultType 直接表示返回类型包括 基础数据类型 和 复杂数据类型。
- SQL映射文件的select的返回结果类型的映射可以使用resultMap和resultType,但不能同时使用。
- resultMap 则是对外部resultMap定义的引用,对外部resultMap的Id,表示返回结果映射到哪一个resultMap上。【它的应用场景一般是 数据库字段信息与对象属性不一致 或者需要做复杂的联合查询以便自由控制映射结果。】
查询语句示例:
<select id="count" resultType="int">
select count(id) from smbms_user
</select>
id为对应方法名,resultType为返回值类型
单个参数select
示例:
接口方法:
//根据用户名称查询用户列表(模糊查询)
List<User> getUserListByUserName(String UserName);
Sql映射文件代码:
<!-- 根据用户名称查询用户列表(模糊查询) -->
<select id="getUserListByUserName" parameterType="String" resultType="User">
select * from smbms_user where userName LIKE concat('%',#{UserName},'%')
</select>
解析:
这里要接收的是User的集合,但是不要以为返回值类型也是一个集合,返回值类型写集合会报错的,只需要写User对象即可,当返回结果有多个对象MyBatis会自动包装成集合。
模糊查询要用concat()连接,字符串连接,将多个字符串拼接起来,用,隔开。
参数是字段都用#{}包裹,表示预编译参数化。在SQL中会自动将参数用单引号包裹。
对象(实体类)参数select
示例:
接口方法:
//根据用户名称和用户角色查询用户列表(实体类)
List<User> getUserListByPojo(User users);
Sql映射文件代码:
<!-- 根据用户名称和用户角色查询用户列表(实体类) -->
<select id="getUserListByPojo" parameterType="User" resultType="User">
select * from smbms_user where UserName like concat('%',#{userName},'%') and userRole=#{userRole}
</select>
测试类代码:
//根据用户名称和用户角色查询用户列表(实体类)
@Test
public void getUserListByPojo(){
SqlSession session=null;
User users=new User();
users.setUserName("赵");
users.setUserRole(3);
try {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sf=new SqlSessionFactoryBuilder().build(is);
session=sf.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
List<User> userList=userMapper.getUserListByPojo(users);
for(User user : userList){
System.out.println("用户名称"+user.getUserName());
}
} catch (Exception e) {
// TODO: handle exception
}finally{
if(session!=null){
session.close();
}
}
}
解析:
用实体类对象作为参数时parameterType填写对象名称,#{}参数填写对象的属性名称即可。
使用@Param注解实现多个参数传入
示例:
接口方法:
//根据用户名称和用户角色查询用户列表(多参)
List<User> getUserListByPojo1(
@Param("userName")String userName,
@Param("userRole")Integer userRole);
Sql映射文件代码:
<!-- 根据用户名称和用户角色查询用户列表(多参) -->
<select id="getUserListByPojo1" resultType="User">
select * from smbms_user where UserName like concat('%',#{userName},'%') and userRole=#{userRole}
</select>
测试类代码:
//根据用户名称和用户角色查询用户列表(实体类)
@Test
public void getUserListByPojo1(){
SqlSession session=null;
String userName="赵";
Integer userRole=3;
try {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sf=new SqlSessionFactoryBuilder().build(is);
session=sf.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
List<User> userList=userMapper.getUserListByPojo1(userName,userRole);
for(User user : userList){
System.out.println("用户名称:"+user.getUserName());
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
}
解析:
用多参传入时不需要写parameterType参数类型,#{}参数填写@Param注解括号中的别名即可。参数固定的业务方法,可以使用多参数入参。但是一般多参时如果是对象属性中有的推荐用对象参入,如果参数是对象属性中没有的把参数封装成Map然后Map参入。实际开发中随机应变。下面就实现Map参入。
使用Map参入
示例:
接口方法:
//根据用户名称和用户角色查询用户列表(map)
List<User> getUserListByMap(Map<String, Object> map);
Sql映射文件代码:
<!-- 根据用户名称和用户角色查询用户列表(map参入)-->
<select id="getUserListByMap" parameterType="map" resultType="User" >
select * from smbms_user where UserName like concat('%',#{un},'%') and userRole=#{ur}
</select>
测试类代码:
//根据用户名称和用户角色查询用户列表(map)
@Test
public void getUserListByMap(){
System.out.println(1);
SqlSession session=null;
Map<String, Object> map=new HashMap<String, Object>();
map.put("un", "赵");
map.put("ur", 3);
try {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sf=new SqlSessionFactoryBuilder().build(is);
session=sf.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
List<User> userList=userMapper.getUserListByMap(map);
for(User user : userList){
System.out.println("用户名称:"+user.getUserName());
}
} catch (Exception e) {
// TODO: handle exception
}finally{
if(session!=null){
session.close();
}
}
}
解析:
用Map参入时parameterType参数类型为map,#{}参数填写Map对应的Key值。这种方法很灵活,不管是什么参数类型,有多少个参数,都可以把他封装成Map进行入参,通过map的Key(键)来获取。
使用resultMap完成查询
实际开发中,要查询的表肯定不止一个,大部分都是多表查询,比如上面的示例当中都查询了用户角色,但是用户表只有用户角色的Id,那么这个时候就要多表连接了,问题来了,多张表的数据,和实体类的属性是无法对应的,那怎么办,有以下几种方法。
- 增加别名(pojo):可以修改sql语句和pojo,可以使用resultType做自动映射(需要注意的是:字段名和属性名是否一致,若不一致,那么需要给字段起别名,变成一致),对于列表要求显示指定字段(可以通过sql查询字段来控制)
- 通过resultMap来映射自定义结果 推荐第二中方法,resultmap做自定义映射结果,字段名可以不一致,并且还可以指定要显示的列,比较灵活,运用页比较广泛。
注意:两种方法都需要在实体类中添加需要的属性
增加别名示例:
示例:
接口方法:
//根据用户名称和用户角色查询用户列表和用户角色(更改实体类)
List<User> getUserListByPojo2(User users);
Sql映射文件代码:
<select id="getUserListByPojo2" parameterType="User" resultType="User">
select u.*,r.roleName as userRoleName
from smbms_user u,smbms_role r
where u.userRole=r.id
and u.userRole=#{userRole}
and u.userName like concat('%',#{userName},'%')
</select>
测试类代码:
//根据用户名称和用户角色查询用户列表和用户角色(更改实体类)
@Test
public void getUserListByPojo2(){
SqlSession session=null;
User users=new User();
users.setUserName("赵");
users.setUserRole(3);
try {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sf=new SqlSessionFactoryBuilder().build(is);
session=sf.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
List<User> userList=userMapper.getUserListByPojo2(users);
for(User user : userList){
System.out.println("用户名称:"+user.getUserName());
System.out.println("用户角色名称:"+user.getUserRoleName());
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
}
解析:
在实体类添加需要的字段属性,然后在Sql映射文件编写SQL语句,注意这里查询出来的列名和属性名称不同是没办法将这个结果自动映射到对象中,所以要给此列名添加一个别名和字段名称相同。
resultMap方法示例:
接口方法和测试类代码一样这里讲解SQL映射文件的代码
Sql映射文件代码:
<resultMap type="user" id="userList">
<id property="id" column="id"/>
<result property="userName" column="userName"/>
<result property="userRoleName" column="roleName"/>
</resultMap>
<select id="getUserListByPojo3" parameterType="User" resultMap="userList">
select u.*,r.roleName
from smbms_user u,smbms_role r
where u.userRole=r.id
and u.userRole=#{userRole}
and u.userName like concat('%',#{userName},'%')
</select>
解析:
resultMap元素属性和自节点:
id属性:唯一标识,此id值用于select元素resultMap属性的引用。
type属性: 表示该resultMap的映射结果类型。
id子节点:用来标识主键,提高性能。
result子节点:用来标识其中的字段属性,其中column属性标识从数据库查询到的字段名,property表示查询出来的字段对应的值赋给实体对象的那个字段属性。
resultType和resultMap的关联和区别?
- resultType :直接表示返回类型,包括基本数据类型和复杂数据类型。
- resultMap :对外部resultMap的引用。它的应用场景一般是:(1)数据库字段信息与对象属性不一致 (2) 复杂的联合查询,自由控制映射结果
- resultType和resultMap关联:
MyBatis查询的时候,其实查询出来的值都存放在一个 Map 中,其中 键 是字段名,值则是对应的值。当 select 提供的返回类型属性为 resultType的时候,MyBatis会将Map里面的键值对取出赋给 resultType所指定的对象对应的属性(即调用对应的对象里的属性的set()方法进行填充)。由此可以看出其实MyBatis的每个查询映射的返回类型都是resultMap。只是resultType里填的对象可以看成是一个系统已经定义好的resuletMap。而我们自己定义的resultMap就是一个根据实际情况自定义的resultMap。
而我们上面该别名的方式正是因为MyBatis的自动映射的resultMap键值对无法找到对应的属性进行填充,所以给一个别名让它对应。自定义resultMap同理,告诉MyBatis应该把这个键值对给谁。
MyBatis实现增删改
我们前面学习了SQL映射文件的使用实现查询操作了,增删改学习起来就简单了。同理都是一个原理,只是元素标签不同罢了。
注意:insert、update、delete元素均没有resultType属性,因为他们的结果都是受影响行数,返回结果都是int
其中属性:
(1)id属性:命名空间中唯一的标识,可以被用来引用这条语句
(2)parameterType属性:传入的参数类型
<insert id="add" parameterType="User" >
insert into smbms_user (userCode,userName,userPassword)
values ( #{userCode},#{userName},#{userPassword})
</insert>
delete from smbms_user where id = #{id} ``` ``` update smbms_user set userCode = #{userCode}, userName = #{userName}, userPassword = #{userPassword} where id = #{id} # 使用resultMap实现高级映射结果 ## association association:映射到某个"复杂类型"属性,比如JavaBean类,即JavaBean内部嵌套复杂数据类型(JavaBean)属性,这种情况就属于复杂类型的关联,需要注意Association是一对一关联关系。简单点说比如有一个实体类当中定义了另一个实体类。 **association的属性及子节点:** javaType属性:完整的Java类名或者别名 property属性:映射数据库列的实体对象的属性 id子节点:主键 result子节点:
- property:嵌套实体对象的属性名称
- column:数据库列名或者别名
示例:
实体类:添加javaBean对象
private Role role; //用户角色
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
接口方法:
//根据用户名称和用户角色查询用户列表和用户角色(resultMap的association方式)
List<User> getUserListByPojoTest(User users);
Sql映射文件代码:
<resultMap type="user" id="userRoleList">
<id property="id" column="id"/>
<result property="userName" column="userName"/>
<association property="role" javaType="Role">
<id property="id" column="id"/>
<result property="roleName" column="roleName"/>
</association>
</resultMap>
<select id="getUserListByPojoTest" parameterType="User" resultMap="userRoleList">
select u.*,r.roleName
from smbms_user u,smbms_role r
where u.userRole=r.id
and u.userRole=#{userRole}
and u.userName like concat('%',#{userName},'%')
</select>
测试类代码:
//根据用户名称和用户角色查询用户列表和用户角色(resultMap的association方式)
@Test
public void getUserListByPojoTest() {
SqlSession session = null;
User users = new User();
users.setUserName("赵");
users.setUserRole(3);
try {
InputStream is = Resources
.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);
session = sf.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserListByPojoTest(users);
for (User user : userList) {
System.out.println("用户名称:" + user.getUserName());
System.out.println("用户角色名称:" + user.getRole().getRoleName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
collection
collection:collection元素作用和association元素差不多一样,也是映射到JavaBean的某个复杂数据类型属性,只不过这个属性是一个集合列表或者数组。collection是一对多特性。
association的属性及子节点和association差不多,就一个javaType和ofType的区别,意思都一样
示例:
实体类:添加list集合
private List<Address> addressList;//用户地址,集合
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
接口方法:
//查询指定用户信息及其地址列表
User getUserAddressList(Integer id);
Sql映射文件代码:
<resultMap type="user" id="userAddressList">
<id property="id" column="id"/>
<result property="userName" column="userName"/>
<collection property="addressList" ofType="Address">
<id property="id" column="a_id"/>
<result property="addressDesc" column="addressDesc"/>
</collection>
</resultMap>
<select id="getUserAddressList" parameterType="int" resultMap="userAddressList">
select u.*,a.id as a_id,a.addressDesc
from smbms_user u,smbms_address a
where u.id=a.userId
and u.id=#{id}
</select>
测试类代码:
//查询指定用户信息及其地址列表
@Test
public void getUserAddress() {
SqlSession session = null;
Integer id=1;
try {
InputStream is = Resources
.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);
session = sf.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUserAddressList(id);
System.out.println("用户名称:"+user.getUserName());
for(Address address : user.getAddressList()){
System.out.println("用户地址:"+address.getAddressDesc());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
resultMap自动映射级别和MyBatis缓存
resultMap自动映射级别
前面讲解了resultMap的用法,但是有一个问题还没有讲解到,就是在resultMap中如果添加了association和collection元素而没有配置某一个属性,那么resultMap是不会自动映射到那个属性的,也就是不会赋值给那个属性。示例:
<resultMap type="user" id="userRoleList">
<id property="id" column="id"/>
<!-- <result property="userName" column="userName"/> -->
<association property="role" javaType="Role">
<id property="id" column="id"/>
<result property="roleName" column="roleName"/>
</association>
</resultMap>
我将userName注释了,那么就算在数据库拿到这个userName的数据了,但是是不会自动映射赋值给这个属性的,为null,因为MyBatis此时根本在这个resultMap中找不到这个属性。
这是因为resultMap映射级别autoMappingBehavior默认情况下是(PARTIAL),若是普通的数据类型的属性,会自动匹配,但是内部嵌套了(association或collection),那么它是不会自动匹配的,也就没办法拿到值。除非在配置文件中手工设置autoMappingBehavior的value为FULL(自动匹配所有)。
修改mybatis-config.xml代码如下:
<settings>
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
resultMap自动映射三个匹配级别:
- NONE:禁止自动匹配
- PARTIAL(默认):自动匹配所有属性,有内部嵌套(association、collection)的除外。
- FULL:自动匹配所有。
MyBatis缓存
缓存也就是将数据放在哪里的意思
一级缓存
一级缓存是本地缓存,作用范围为session域内,当session关闭之后,该session中的所有cache会被清空。
二级缓存
二级缓存是超出session范围之外的,可以被所有SqlSession共享。开启它需要在配置文件中设置。
二级缓存配置步骤:
(1)在配置文件mybatis-config.xml中设置:
<settings>
<setting name="cacheEnabled" value="true">
<settings>
(2)在Mapper文件(如UserMapper.xml)中设置缓存,默认情况下是没有开启缓存的。
<cache eviction="FIFO" flushInterval="6000" size="512" readOnly="true"/>
其中eviction="FIFO"表示的是缓存策略为先进先出,也就是如果数据放不下了就会把前面的挤出去。flushInterval="6000"表示的是没1分钟6000毫秒刷新一次这个缓存。size="512"表示这个缓存的长度为512个对象。 readOnly="true"为只读。
(3)在mapper文件配置支持cache后,如果需要将某一个查询配置到这个缓存中还需要设置cache。代码如下:
<select id="count" resultType="int" useCache="true">
.....
</select>
useCache="true"表示将这个查询结果放入这个缓存中。