今天学习http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html。关于mapper.xml的sql语句的使用。

项目路径:https://github.com/chenxing12/l4mybatis

  1. 创建项目
  2. select
  3. insert
  4. update
  5. delete
  6. insert\update\delete参数说明
  7. sql代码段
  8. 参数
  9. 高级查询
    1. id&reulst
    2. 构造方法
    3. 关联查询

首先,准备环境。

1.创建project

在parent项目上右键,new model->maven->mybatis-mapper.

填充pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>l4mybatis</artifactId>
<groupId>com.test</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>mytatis-mapper</artifactId> <dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies> <build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build> </project>

在resources下添加log4j.properties:

log4j.rootLogger=DEBUG, stdout, logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=log/test.log
log4j.appender.logfile.MaxFileSize=128MB
log4j.appender.logfile.MaxBackupIndex=3
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %c.%M(%L) - %m%n

在resources下添加mybatis-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>
<properties resource="db.properties"/> <typeAliases>
<package name="com.test.mapper.model"/>
</typeAliases> <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
<mappers>
<mapper resource="com.test.mapper.mapper/PersonMapper.xml"/>
</mappers>
</configuration>

在resources下添加db.properties:

#jdbc.driver=com.mysql.jdbc.Driver
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&useSSL=false
jdbc.username=root
jdbc.password=123456

在数据库mybatis中创建一个person表:

/*
Navicat MySQL Data Transfer Source Server : localhost
Source Server Version : 50605
Source Host : localhost:3306
Source Database : mybatis Target Server Type : MYSQL
Target Server Version : 50605
File Encoding : 65001 Date: 2016-07-06 22:22:34
*/ SET FOREIGN_KEY_CHECKS=0; -- ----------------------------
-- Table structure for person
-- ----------------------------
DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of person
-- ----------------------------
INSERT INTO `person` VALUES ('1', 'Ryan');

在resources下创建com.test.mapper.mapper/PersonMapper.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="com.test.mapper.dao.PersonMapper">
<select id="selectPerson" parameterType="int" resultType="hashmap">
select * from person where id = #{id}
</select> </mapper>

在java下新建com.test.mapper.dao.PersonMapper.java:

package com.test.mapper.dao;

import java.util.HashMap;

/**
* Created by miaorf on 2016/7/6.
*/
public interface PersonMapper { HashMap selectPerson(int id);
}

在java下添加:com.test.mapper.model.Person:

package com.test.mapper.model;

import java.io.Serializable;

/**
* Created by miaorf on 2016/7/6.
*/
public class Person implements Serializable { private Integer id;
private String name; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

测试环境:

在test下创建com.test.mapper.dao.PersonMapperTest:

package com.test.mapper.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 org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import java.io.IOException;
import java.util.HashMap; import static org.junit.Assert.*; /**
* Created by miaorf on 2016/7/6.
*/
public class PersonMapperTest {
private SqlSession sqlSession;
private static SqlSessionFactory sqlSessionFactory; @BeforeClass
public static void init() throws IOException {
String config = "mybatis-config.xml";
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(config));
} @Before
public void setUp() throws Exception {
sqlSession = sqlSessionFactory.openSession();
} @Test
public void selectPerson() throws Exception {
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
HashMap map = mapper.selectPerson(1);
System.out.println(map);
} }

运行:

2016-07-06 22:23:31,962 DEBUG [org.apache.ibatis.logging.LogFactory] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2016-07-06 22:23:32,128 DEBUG [org.apache.ibatis.io.VFS] - Class not found: org.jboss.vfs.VFS
2016-07-06 22:23:32,129 DEBUG [org.apache.ibatis.io.JBoss6VFS] - JBoss 6 VFS API is not available in this environment.
2016-07-06 22:23:32,131 DEBUG [org.apache.ibatis.io.VFS] - Class not found: org.jboss.vfs.VirtualFile
2016-07-06 22:23:32,132 DEBUG [org.apache.ibatis.io.VFS] - VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
2016-07-06 22:23:32,134 DEBUG [org.apache.ibatis.io.VFS] - Using VFS adapter org.apache.ibatis.io.DefaultVFS
2016-07-06 22:23:32,135 DEBUG [org.apache.ibatis.io.DefaultVFS] - Find JAR URL: file:/D:/workspace/mybatis/l4mybatis/mytatis-mapper/target/classes/com/test/mapper/model
2016-07-06 22:23:32,135 DEBUG [org.apache.ibatis.io.DefaultVFS] - Not a JAR: file:/D:/workspace/mybatis/l4mybatis/mytatis-mapper/target/classes/com/test/mapper/model
2016-07-06 22:23:32,213 DEBUG [org.apache.ibatis.io.DefaultVFS] - Reader entry: Person.class
2016-07-06 22:23:32,214 DEBUG [org.apache.ibatis.io.DefaultVFS] - Listing file:/D:/workspace/mybatis/l4mybatis/mytatis-mapper/target/classes/com/test/mapper/model
2016-07-06 22:23:32,214 DEBUG [org.apache.ibatis.io.DefaultVFS] - Find JAR URL: file:/D:/workspace/mybatis/l4mybatis/mytatis-mapper/target/classes/com/test/mapper/model/Person.class
2016-07-06 22:23:32,215 DEBUG [org.apache.ibatis.io.DefaultVFS] - Not a JAR: file:/D:/workspace/mybatis/l4mybatis/mytatis-mapper/target/classes/com/test/mapper/model/Person.class
2016-07-06 22:23:32,217 DEBUG [org.apache.ibatis.io.DefaultVFS] - Reader entry: ���� 1 6
2016-07-06 22:23:32,220 DEBUG [org.apache.ibatis.io.ResolverUtil] - Checking to see if class com.test.mapper.model.Person matches criteria [is assignable to Object]
2016-07-06 22:23:32,306 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections.
2016-07-06 22:23:32,307 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections.
2016-07-06 22:23:32,309 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections.
2016-07-06 22:23:32,310 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections.
2016-07-06 22:23:32,511 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Opening JDBC Connection
2016-07-06 22:23:32,842 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 733672688.
2016-07-06 22:23:32,842 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2bbaf4f0]
2016-07-06 22:23:32,847 DEBUG [com.test.mapper.dao.PersonMapper.selectPerson] - ==> Preparing: select * from person where id = ?
2016-07-06 22:23:32,911 DEBUG [com.test.mapper.dao.PersonMapper.selectPerson] - ==> Parameters: 1(Integer)
2016-07-06 22:23:32,946 DEBUG [com.test.mapper.dao.PersonMapper.selectPerson] - <== Total: 1
{name=Ryan, id=1}

  

2.select

查询语句。负责拼接查询语句并将查询结果映射出来。上例中:

<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
  • 这个语句的id为selectPerson,这个就是对应mapper接口中的方法的名字。
  • parameterType是输入参数类型为int。
  • resultType表示查询结果映射为HashMap
  • #{id}是占位符,相当于JDBC中采用PreparedStatement时sql语句中的问号,表示参数名为id的参数值会替换这个位置。

注意到mapper.xml的namespace就是指向所对应的mapper 接口:

<mapper namespace="com.test.mapper.dao.PersonMapper">

在mapper接口中的方法要和mapper.xml中的id所一一对应。因此,这个查询的节点对应的mapper接口的方法为:

public interface PersonMapper {

    HashMap selectPerson(int id);
}

  

事实上,select节点的可选参数有以下几种:

<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">

文档对各个参数含义给出了解释:

Select Attributes
属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
fetchSize 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。  

3.Insert

首先,准备数据库:

CREATE TABLE `author` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(40) NOT NULL DEFAULT '',
`password` varchar(128) NOT NULL DEFAULT '',
`email` varchar(40) NOT NULL DEFAULT '',
`bio` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

然后,编写对应实体:com.test.mapper.model.Author

package com.test.mapper.model;

import java.io.Serializable;

/**
* Created by miaorf on 2016/7/7.
*/
public class Author implements Serializable { private Integer id;
private String username;
private String password;
private String email;
private String bio; public Author() {
} public Author(String username, String password, String email, String bio) {
this.username = username;
this.password = password;
this.email = email;
this.bio = bio;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public String getBio() {
return bio;
} public void setBio(String bio) {
this.bio = bio;
}
}

编写mapper接口:com.test.mapper.dao.AuthorMapper

package com.test.mapper.dao;

import com.test.mapper.model.Author;

import java.util.HashMap;
import java.util.List; /**
* Created by miaorf on 2016/7/6.
*/
public interface AuthorMapper { int insertAuthor(Author author); int insertAuthors(List<Author> list);
}

编写mapper.xml:com.test.mapper.mapper/AuthorMapper.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="com.test.mapper.dao.AuthorMapper">
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
INSERT INTO author(username,password,email,bio)
VALUES (#{username},#{password},#{email},#{bio})
</insert> <insert id="insertAuthors" useGeneratedKeys="true" keyProperty="id">
INSERT INTO author(username,password,email,bio) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.username},#{item.password},#{item.email},#{item.bio})
</foreach>
</insert> </mapper>

编写测试用例:com.test.mapper.dao.AuthorMapperTest

package com.test.mapper.dao;

import com.test.mapper.model.Author;
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.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import static org.junit.Assert.*; /**
* Created by miaorf on 2016/7/7.
*/
public class AuthorMapperTest {
private SqlSession sqlSession;
private static SqlSessionFactory sqlSessionFactory; @BeforeClass
public static void init() throws IOException {
String config = "mybatis-config.xml";
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(config));
} @Before
public void setUp() throws Exception {
sqlSession = sqlSessionFactory.openSession();
} @Test
public void insertAuthorTest() throws Exception{
AuthorMapper mapper = sqlSession.getMapper(AuthorMapper.class);
Author ryan = new Author("Ryan", "123456", "qweqwe@qq.com", "this is a blog");
int result = mapper.insertAuthor(ryan);
sqlSession.commit(); assertNotNull(ryan.getId());
} @Test
public void insertAuthorsTest() throws Exception{
AuthorMapper mapper = sqlSession.getMapper(AuthorMapper.class);
List<Author> list = new ArrayList<Author>();
for (int i = 0; i < 3; i++) {
list.add(new Author("Ryan"+i, "123456", "qweqwe@qq.com", "this is a blog"));
} int result = mapper.insertAuthors(list);
sqlSession.commit(); assertNotNull(list.get(2).getId());
} }

以上就是一个简单的插入了,数据库为支持主键自增的,比如Mysql。可以返回主键到对应实体中。下面来分析每个参数:

3.1 AuthorMapperTest

前文讲过mybatis的SqlSessionFactory 的最佳范围是应用范围。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。所以,这个必须是要可以暴露出啦的bean。每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

因此,将SqlSessionFactory设置成静态成员变量,在类初始化的时候就加载。而SqlSession则要在每次请求数据库的时候生成,这里放到Before里是为了复用在多个Test中,如果是在一个dao中,就不可以将SqlSession放到成员变量了,应该为局部变量。

3.2 insert 返回值

通过获取mapper接口,然后调用方法来实现sql的执行。insert方法的返回值是整形,对应mysql中执行成功的结果数。插入一条成功则返回1,插入n条成功,则返回n,插入失败,返回0. 当然,失败通常会抛出异常。这个在更新的时候有用,如果不需要更新,则返回0,更新成功n条返回n。

3.3 AuthorMapper.xml

使用完全限定名来指定对应的mappr接口:

<mapper namespace="com.test.mapper.dao.AuthorMapper">

用insert节点来编写插入语句:

 <insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
INSERT INTO author(username,password,email,bio)
VALUES (#{username},#{password},#{email},#{bio})
</insert>
  • id表示mapper接口的方法名,要一致
  • useGeneratedKeys="true"表示数据库支持主键自增
  • keyProperty="id"表示自增字段为id,这个属性和useGeneratedKeys配合使用
  • 发现这里没有指定parameterType,因为mybatis可以通过 TypeHandler 推断出具体传入语句的参数
  • 也没有指定resultType,MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题

到这里是理想插入,但如果其中一个字段是null,而我们设置数据库字段不允许为null,则会抛出异常。也就是说,在执行这条语句前一定要检查各项参数是否满足要求。

 @Test(expected = PersistenceException.class)
public void insertAuthorExceptionTest() throws Exception{
AuthorMapper mapper = sqlSession.getMapper(AuthorMapper.class);
Author ryan = new Author("Ryan", null, "qweqwe@qq.com", "this is a blog");
int result = mapper.insertAuthor(ryan);
sqlSession.commit(); assertNotNull(ryan.getId());
}

  

4.update

更新前需要先有一个实体,这里先来查询一个出来:

4.1添加selectById

在com.test.mapper.mapper/AuthorMapper.xml中添加一个select节点:

<select id="selectAuthorById" resultType="author">
SELECT * FROM author WHERE id = #{id}
</select>

和insert不同的是,这里必须制定resultType或者resultMap,否则无法映射查询结果。

在com.test.mapper.dao.AuthorMapper中添加对应的方法:

Author selectAuthorById(int id);

  

添加测试用例:

@Test
public void selectAuthorByIdTest() throws Exception{
AuthorMapper mapper = sqlSession.getMapper(AuthorMapper.class);
Author author = mapper.selectAuthorById(3);
assertNotNull(author);
}

  

4.2 添加update节点

AuthorMapper.xml:

<update id="updateAuthor" >
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>

  

AuthorMapper.java:

int updateAuthor(Author author);

  

AuthorMapperTest.java:

@Test
public void TestUpdateAuthorEffective() throws Exception{
Author author = mapper.selectAuthorById(3);
author.setBio("I have changed the bio");
int i = mapper.updateAuthor(author);
sqlSession.commit();
assertTrue(i>0);
}
@Test
public void TestUpdateAuthorInEffective() throws Exception{
Author author = mapper.selectAuthorById(3);
author.setId(1024);
int i = mapper.updateAuthor(author);
sqlSession.commit();
assertTrue(i==0);
}

  

update和insert一样,mybatis自动判断输入和输出。

5.delete

添加delete节点:

<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>

  

添加delete方法:

int deleteAuthor(int id);

  

添加delete测试:

@Test
public void TestDeleteAuthorEffective() throws Exception{
int i = mapper.deleteAuthor(3);
assertTrue(i==1);
}
@Test
public void TestDeleteAuthorInEffective() throws Exception{
int i = mapper.deleteAuthor(3000);
assertTrue(i==0);
}

  

delete节点也不用指定输入和输出,当然也可以指定。

6.insert,update,delete参数表

属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句。
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。

7.SQL代码段

为了提高代码重复利用率,mybatis提供了将部分sql片段提炼出来的, 作为公共代码使用。

<sql id="userColumns">
${alias}.id,${alias}.username,${alias}.password,${alias}.email,${alias}.bio
</sql>
<select id="selectAuthor" resultType="map">
SELECT
<include refid="userColumns"><property name="alias" value="t1"/></include>
FROM author t1
</select>

 

List<Author> selectAuthor();

  

@Test
public void TestSelectAuthor() throws Exception{
List<Author> authors = mapper.selectAuthor();
assertTrue(authors.size()>0);
}

  

 8.Parameters(参数)

前文在insert的时候提到过,输入参数不指定,可以通过对象判断出来。然而遇到复杂的输入参数的时候就需要指定.将要传入语句的参数的完全限定类名或别名.

修改insert节点,增加parameterType:

<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id" parameterType="author">
INSERT INTO author(username,password,email,bio)
VALUES (#{username},#{password},#{email},#{bio})
</insert>

parameterType指定为author。这个的全称是:com.test.mapper.model.Author,之所以简写是因为在mybatis-config.xml中配置了名称简化:

<typeAliases>
<package name="com.test.mapper.model"/>
</typeAliases>

  

该配置指定model下的类都可以简写为类名首字母小写。

username,password等属性可以通过查找输入对象Author的getter属性获得。

注意:

  输入参数是简单类型int,short,long,char,byte,boolean,float,double可以直接写基本类型。如果是类,则要写全称,比java.lang.String.

输出参数

输出参数用resultType表示,用来和查询出来的结果集映射。下面是一个简单的查询:

<select id="selectAuthorById" resultType="author">
SELECT * FROM author WHERE id = #{id}
</select>

查询出来的字段和resultType中author映射。

如果列名和bean的属性不一致如何映射?需要使用查询的别名:

<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>

  

还有一种做法是将结果集单独拿出来,使用resultMap而不是resultType:

<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>

对应的,查询语句的返回类型为resultMap:

<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>

  

其实,很容易看出来,resultType和resultMap的作用都是将结果集映射成java bean,因此二者只能使用一个,不然无法确定究竟转换成哪个bean。而当我们涉及联合查询的时候,查询的结果集并没有创建的bean可以映射,这时候resultMap就可以为结果集提供映射方案。下面学习resultMap的各种参数

高级结果映射

1. id&result

在上述查询中,看到了id和result节点。相信很容易就猜出来。id就是表的主键字段映射,result则表示主键以外的映射。标注id对性能缓存等有帮助。

Id and Result Attributes
属性 描述
property 映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同 的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称 property 的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你 可以这样映射一些东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。
column 从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会 传递给 resultSet.getString(columnName)方法参数中相同的字符串。
javaType 一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为。
jdbcType 在这个表格之后的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅 仅需要对插入,更新和删除操作可能为空的列进行处理。这是 JDBC jdbcType 的需要,而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定 这个类型-但仅仅对可能为空的值。
typeHandler 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理 器的实现,或者是类型别名。

2.构造方法

上述方法中,id可以映射主键,result可以映射其他列,貌似已经完全了。也确实完全了,但对于属性注入,构造方法显然是个很好的办法。Mybatis提供了构造方法的映射:

首先,给Author增加一个构造方法:

public Author(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}

然后,在com.test.mapper.mapper/AuthorMapper.xml中添加select:

<resultMap id="authorByConstructor" type="author">
<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="password" javaType="String"/>
</constructor>
<result column="email" property="email"/>
</resultMap>
<select id="selectAuthor2Construct" resultMap="authorByConstructor">
SELECT * FROM author
</select>

添加对应的接口:com.test.mapper.dao.AuthorMapper:

List<Author> selectAuthor2Construct();

测试:

@Test
public void testSelectAuthor2Construct() throws Exception{
List<Author> authors = mapper.selectAuthor2Construct();
assertTrue(authors.get(0).getUsername()!=null);
}

  

3. 关联查询

做查询之前,先修改几个配置。mapper.xml是在mybatis-config.xml中指定,那么我们每增加一个mapper都要增加一个配置,很麻烦。为了简化配置。需要将mapper接口和mapper.xml放到同一个文件下,并且接口和xml文件命名一致。使用mybatis的自动扫描:.这样,当我们新增接口的时候,直接创建接口和对应xml文件就可以了:

<mappers>
<!--<mapper resource="com.test.mapper.dao/AuthorMapper.xml"/>-->
<package name="com.test.mapper.dao"/>
</mappers>

  

3.1prepare

增加一个表blog :

CREATE TABLE `blog` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`author_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 

创建实体类com.test.mapper.model.Blog:

package com.test.mapper.model;

/**
* Created by miaorf on 2016/7/20.
*/
public class Blog {
private Integer id;
private String name;
private Author author; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Author getAuthor() {
return author;
} public void setAuthor(Author author) {
this.author = author;
} @Override
public String toString() {
return "Blog{" +
"id=" + id +
", name='" + name + '\'' +
", author=" + author +
'}';
}
}

创建接口:com/test/mapper/dao/BlogMapper.xml

package com.test.mapper.dao;

import com.test.mapper.model.Blog;

import java.util.List;

/**
* Created by miaorf on 2016/7/20.
*/
public interface BlogMapper { List<Blog> selectBlog(Integer id);
}

创建xml:com/test/mapper/dao/BlogMapper.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="com.test.mapper.dao.BlogMapper">
<resultMap id="blogResult" type="blog">
<association property="author" column="author_id" javaType="author" select="com.test.mapper.dao.AuthorMapper.selectAuthorById"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
select * from blog where id = #{id}
</select> </mapper>

 

3.2含义

首先,修改了mybatis配置文件中mapper扫描配置,因此可以直接在扫描包下添加接口和xml文件。

其次,关于mybatis的命名空间namespace的用法,这个是唯一的,可以代表这个xml文件本身。因此,当我想要引用Author的查询的时候,我可以直接使用AuthorMapper.xml的命名空间点select的id来唯一确定select片段。即:

select="com.test.mapper.dao.AuthorMapper.selectAuthorById"

然后,关联查询,blog的author_id字段和author的id字段关联。因此,将Author作为Blog的一个属性,先查询blog,然后根据author_id字段去查询author。也就是说,查询了两次。这里也是我困惑的地方,为什么mybatis要这样处理,命名可以一次查询取得数据非要两次查询。(待求证)

注意:association节点中

  • column字段是author_id,是当做参数传递给查询Author的查询语句的,如果查询语句的参数有多个则:column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引起 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。
  • property则表示在Blog类中对应的属性。

我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。

其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。

这种方式很简单, 但是对于大型数据集合和列表将不会表现很好。 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:

  • 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
  • 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。

MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。

所以还有另外一种方法

3.3 关联查询结果

上述关联查询主要是为了延迟加载,做缓存用的,如果你不调用blog.getAuthor()来获取author,那么mybatis就不会去查询Author。也就是说,mybatis把博客和作者的查询当做两步来执行。实现信息分段加载,在某些场合是有用的。然而在博客这里,显然不太合适,因为我们看到博客的同时都会看到作者,那么必然会导致查询数据库两次。下面,来测试另一种方式,像sql关联查询一样,一次查出结果。

<select id="selectBlogWithAuthor" resultMap="blogResultWithAuthor">
SELECT
b.id as blog_id,
b.name as blog_title,
b.author_id as blog_author_id,
a.id as author_id,
a.username as author_username,
a.password as author_password,
a.email as author_email,
a.bio as author_bio
FROM blog b LEFT OUTER JOIN author a ON b.author_id=a.id
WHERE b.id = #{id}
</select>
<resultMap id="blogResultWithAuthor" type="blog">
<id property="id" column="blog_id"/>
<result property="name" column="blog_title"/>
<association property="author" column="blog_author_id" javaType="author" resultMap="authorResult"/>
</resultMap>
<resultMap id="authorResult" type="author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</resultMap>

和3.1里的查询一样,都是查询出blog和Author。但这个只查询数据库一次,也就是说实现了我们的关联查询。这几行代码乍一看有点复杂,仔细分析一下就很明了了。

1> 首先看到的是select标签,这个表示查询。其中id表示对应的接口的方法名;resultMap的值是一个resultMap节点的id,这个表示select查询结果的映射          方式。

2> select中间就是我熟悉的关联查询语句,这里不做赘述

3> 然后就是resultMap所指向的节点blogResultWithAuthor。

  • resultMap节点的id就是唯一标识这个节点的,type表示这个resultMap最终映射成的实体类Blog。
  • id是主键映射,这个和mybatis缓存有关。
  • result:
  • association:
  • authorResult同理。

可以看到控制台打印的日志:

2016-07-22 23:01:00,148 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Opening JDBC Connection
2016-07-22 23:01:11,017 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 1893960929.
2016-07-22 23:01:19,281 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@70e38ce1]
2016-07-22 23:01:27,018 DEBUG [com.test.mapper.dao.BlogMapper.selectBlogWithAuthor] - ==> Preparing: SELECT b.id as blog_id, b.name as blog_title, b.author_id as blog_author_id, a.id as author_id, a.username as author_username, a.password as author_password, a.email as author_email, a.bio as author_bio FROM blog b LEFT OUTER JOIN author a ON b.author_id=a.id WHERE b.id = ?
2016-07-22 23:01:29,697 DEBUG [com.test.mapper.dao.BlogMapper.selectBlogWithAuthor] - ==> Parameters: 1(Integer)
2016-07-22 23:01:30,091 DEBUG [com.test.mapper.dao.BlogMapper.selectBlogWithAuthor] - <== Total: 1

Mybatis-mapper-xml-基础的更多相关文章

  1. 【记录】mybatis mapper.xml 基础

    <choose> <when test=""> //... </when> <otherwise> //... </other ...

  2. MyBatis Mapper XML 详解

    MyBatis Mapper XML 详解 MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JD ...

  3. JAVA - Intellij IDEA 中去掉mybatis Mapper.xml背景色

    JAVA - Intellij IDEA 中去掉mybatis Mapper.xml背景色 1:现在公司中使用mybatis的频率非常高,一般都会用MBG来生成基础的代码文件.在intellij中查看 ...

  4. MyBatis Mapper.xml文件中 $和#的区别

    MyBatis Mapper.xml文件中 $和#的区别   网上有很多,总之,简略的写一下,作为备忘.例子中假设参数名为 paramName,类型为 VARCHAR . 1.优先使用#{paramN ...

  5. mybatis mapper.xml 配置文件问题(有的错误xml是不报的) 导致服务无法启动 。

    转载自 开源编程 一舟mybatsi xml编译报错,tomcat启动一直循环,导致内存溢出,启动失败 mapper.xml怎么知道有没有编译错误,哪个位置有错误 这应该是mybatis的一个bug, ...

  6. mybatis mapper xml文件的导入方式和查询方式

    mybatis mapper xml文件的导入方式和查询方式 ssm框架 Mybatis  mapper与SQLSession的关系 每个基于MyBatis的应用都是以一个SqlSessionFact ...

  7. mybatis Mapper.xml和Mapper.java

    mybatis Mapper.xml和Mapper.java 通过Mapper.xml和Mapper.java来实现mybatis.环境和入门的一样的.关键:Mapper.xml + Mapper.j ...

  8. mybatis mapper xml文件配置resultmap时,id行和result行有什么区别?

    mybatis mapper xml文件配置resultmap时,id行和result行有什么区别? <resultMap id = "CashInvoiceMap" typ ...

  9. MyBatis——Mapper XML 文件

    Mapper XML 文件 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单.如果拿它跟具有相同功能的 JDBC 代码进行对比,你会 ...

  10. MyBatis—mapper.xml映射配置

    SQL文件映射(mapper文件),几个顶级元素的配置: mapper元素:根节点只有一个属性namespace(命名空间)作用: 1:用于区分不同的mapper,全局唯一. 2:绑定DAO接口,即面 ...

随机推荐

  1. ButterKnife--View注入框架的使用

    作为一名Android开发,是不是经常厌烦了大量的findViewById以及setOnClickListener代码,而ButterKnife是一个专注于Android系统的View注入框架,让你从 ...

  2. java.sql.SQLException: 关闭的连接

    在Dao接口实现类里面的conn.close()之类的关闭数据库连接的代码注释掉就可以了. 可能还有别的解决方法,不过这样改比较方便.

  3. InstallShield2013 error 6109

    InstallShield在没添加打包文件时是可以编译生成安装包的,在增加打包文件之后就报以下错误: ISEXP : error : -6109: Internal build error ISEXP ...

  4. Eclipse代码格式化规范

    附件()是Eclipse代码格式文件,根据以下步骤导入到Eclipse中,帮助规范代码格式. 导入步骤:1. Window -> Performances2. Java -> Code S ...

  5. Chrome开发者工具不完全指南(五、移动篇)

    前面介绍了Chrome开发者工具的大部分内容工具,现在介绍最后两块功能Audits和Console面板.一.Audits Audits面板会针对目前网页提出若干条优化的建议,这些建议分为两大类,一类是 ...

  6. Echarts使用

    Echarts使用 |版权声明:此文为本人原创,望尊重原创 前言:     和朋友聊天的过程中,朋友提到了Echarts.我当时不知道那个是用来做什么的.回到家我就百度了,开始自己看文档学习.本文是自 ...

  7. salesforce 零基础学习(四十九)自定义列表分页之使用Pagination实现分页效果 ※※※

    上篇内容为Pagination基类的封装,此篇接上篇内容描述如何调用Pagination基类. 首先先创建一个sObject,起名Company info,此object字段信息如下: 为了国际化考虑 ...

  8. 2017预防bug的重要性

    Bug,中文名缺陷.一个让软件测试员兴奋,让开发人员头疼的词.来源二次大战期间,一个称为"马克二型"的计算机,由于天气过热,硬件跟不上导致死机.最后发现是因为飞蛾,被继电器电死,将 ...

  9. C语言的标准输入输出

    1. 标准输入输出 标准输入.输出主要由缓冲区和操作方法两部分组.缓冲区实际上可以看做内存中的字符串数组,而操作方法主要是指printf.scanf.puts.gets,getcha.putcahr等 ...

  10. Java环境变量

    很简单的问题!配置Java环境变量 准备 下载安装Java,官方地址 打开系统环境变量,我的电脑右键 JAVA_HOME 添加JAVA_HOME,设置jdk安装目录,比如"C:\Progra ...