Mybatis框架


MyBatis 是一款优秀的持久层框架,它支持定制化 SQL(灵活)、存储过程(PLSQL模块化的组件,数据库的一部分)以及高级映射(表映射为Bean也可以将Bean映射为表)。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口(框架的思想都是面向接口来编程)和 Java 的 POJO(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

Mybatis 是一个持久层的架构,是 apache 下的顶级项目。
Mybatis 原先是托管在 googlecode 下,再后来是托管在 Github 上。
Mybatis 让程序员将主要的精力放在 sql 上,通过 Mybatis 提供的映射方式,自由灵活生成(半自动,大部分需要程序员编写 sql )满足需要 sql 语句。
Mybatis 可以将向 preparedStatement 中的输入参数自动进行输入映射,将查询结果集灵活的映射成 java 对象。(输出映射)

Mybatis 框架结构图


说明:

  • SqlMapConfig.xml (Mybatis的全局配置文件,名称不定)配置了数据源、事务等 Mybatis 运行环境
  • Mapper.xml 映射文件(配置 sql 语句)
  • SqlSessionFactory (会话工厂)根据配置文件配置工厂、创建 SqlSession
  • SqlSession (会话)面向用户的接口、操作数据库(发出 sql 增删改查)
  • Executor (执行器)是一个接口(基本执行器、缓存执行器)、SqlSession 内部通过执行器操作数据库
  • Mapped Statement (底层封装对象)对操作数据库存储封装,包括 sql 语句、输入参数、输出结果类型

Mybatis工程需要引用Maven的本地仓库,关于Maven本地仓库的配制可以参考
地址:https://www.jianshu.com/p/e351f8e22c9a

Mybatis入门程序
1、需求
实现以下功能:

  • 根据用户id查询一个用户信息
  • 根据用户名称模糊查询用户信息列表
  • 添加用户
  • 更新用户
  • 删除用户

2、环境

  • java 环境 :jdk1.8.0_77
  • 开发工具 : IDEA 2016.1
  • 数据库 : MySQL 5.7
  • Mybatis 运行环境( jar 包)
  • MySQL 驱动包
  • 其他依赖包

数据库表结构如下:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

INSERT INTO `user` VALUES (1, '王五', NULL, '2', NULL);
INSERT INTO `user` VALUES (10, '张三', '2014-7-10', '1', '北京市');
INSERT INTO `user` VALUES (16, '张小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES (22, '陈小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES (24, '张三丰', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES (25, '陈小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES (26, '王五', NULL, NULL, NULL);
INSERT INTO `user` VALUES (28, '陈小明111', '2017-9-14', '1', '式');
INSERT INTO `user` VALUES (29, 'tom123', '2018-1-19', '1', 'shenyang');
INSERT INTO `user` VALUES (30, 'tom123', '2018-1-19', '1', 'shenyang');
INSERT INTO `user` VALUES (31, 'aabbcc', '2018-6-12', 'm', 'neusoft');

1.创建一个Maven工程
2.在pom.xml中添加项目的依赖库(mybatis)

<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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.foreknow</groupId>
  <artifactId>foreknow_mybatis</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>foreknow_mybatis</name>
  <description>foreknow_mybatis</description>
  <dependencies>
   <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <version>1.3</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
  </dependencies>
</project>

3.需要在resources中添加配制文件:db.properties、log4j.properties、SqlMapConfig.xml(Mybatis的核心配制文件)

db.properties配制

#jdbc.driver=oracle.jdbc.driver.OracleDriver
#jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl
#jdbc.username=scott
#jdbc.password=tiger

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybaits?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=

log4j.properties配制

# Global logging configuration
#\u5728\u5f00\u53d1\u73af\u5883\u4e0b\u65e5\u5fd7\u7ea7\u522b\u8981\u8bbe\u7f6e\u6210DEBUG\uff0c\u751f\u4ea7\u73af\u5883\u8bbe\u7f6e\u6210info\u6216error
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

SqlMapConfig.xml(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="db.properties">
    </properties>
    <!-- 全局配置参数,需要时再设置 -->
    <!-- <settings>
    
    </settings> -->
    <!--起别名  -->
    <typeAliases>
        <!-- <typeAlias type="com.neusoft.mybatis.pojo.User" alias="User"/> -->
    </typeAliases>

    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事务管理,事务控制由mybatis-->
            <transactionManager type="JDBC" />
        <!-- 数据库连接池,由mybatis管理-->
            <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="mapper/User.xml" />
    </mappers>
    
</configuration>

4.创建User.java

package com.foreknow.bean;

import java.util.Date;

public class User {
    private int id;
    private String username;
    private String sex;
    private Date birthday;
    private String address;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}


5.创建一个映射(Mapper)文件对数据库进行操作User.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命名空间 ,它的作用是对SQL进行分类化管理 -->
<mapper namespace="test">
    <!--
    Java代码:
        public User findUserById(int id)
    
    id:唯一标识  相当于方法的名称
    parameterType:输入参数的类型  相当于方法的参数类型
    resultType:方法返回值的类型    注意:全路径(包名+类名)
    #{id}:相当于一个占位符
      -->
    <select id="findUserById" parameterType="int" resultType="com.foreknow.bean.User" >
        select * from user where id=#{id}
    </select>
    
    <!-- public void insertUser(User user) -->
    <insert id="insertUser" parameterType="com.foreknow.bean.User">
        insert into User(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>
    <!--  
    ${value}表示拼接sql字符串
    -->
    <select id="findUserByName" parameterType="java.lang.String" resultType="com.foreknow.bean.User">
        select * from user where username like '%${value}%'
    </select>
    
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <update id="updateUser" parameterType="com.foreknow.bean.User">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    </update>
    
</mapper>

6.创建JUnit测试类

package com.foreknow.mybatis_test;


import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
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.junit.Test;

import com.foreknow.bean.User;

public class MybatisUserTest {
    @Test
    public void findUserByIdTest() throws IOException {
        String  resource = "SqlMapConfig.xml";
        //如何对SqlMapConfig.xml读取并解析
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建一个会话工厂(SqlSessionFactory),传入mybatis中的配制信息
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取到SqlSession对象,作用是操作数据库
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //关联User.xml文件进行操作
        User user = sqlSession.selectOne("test.findUserById", 1);
        System.out.println("---------"+user.getUsername()+"-----------");
        //释放资源
        sqlSession.close();
    }
    
    @Test
    public void insertUserTest() throws IOException {
        String  resource = "SqlMapConfig.xml";
        //如何对SqlMapConfig.xml读取并解析
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建一个会话工厂(SqlSessionFactory),传入mybatis中的配制信息
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取到SqlSession对象,作用是操作数据库
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //关联User.xml文件进行操作
        User user = new User();
        user.setUsername("tom_test");
        user.setBirthday(new Date());
        user.setSex("1");
        user.setAddress("foreknow");
        sqlSession.insert("test.insertUser", user);
        //提交事物
        sqlSession.commit();
        //释放资源
        sqlSession.close();
    }
    
    @Test
    public void findUserByNameTest() throws IOException {
        String  resource = "SqlMapConfig.xml";
        //如何对SqlMapConfig.xml读取并解析
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建一个会话工厂(SqlSessionFactory),传入mybatis中的配制信息
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取到SqlSession对象,作用是操作数据库
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //关联User.xml文件进行操作
        
        List<User> list = sqlSession.selectList("test.findUserByName", "张");
        System.out.println(list.size());
        //释放资源
        sqlSession.close();
    }
    
    @Test
    public void deleteUserTest() throws IOException {
        String  resource = "SqlMapConfig.xml";
        //如何对SqlMapConfig.xml读取并解析
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建一个会话工厂(SqlSessionFactory),传入mybatis中的配制信息
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取到SqlSession对象,作用是操作数据库
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //关联User.xml文件进行操作
        sqlSession.delete("test.deleteUser", 33);
        sqlSession.commit();
        //释放资源
        sqlSession.close();
    }
    
    @Test
    public void updateUserTest() throws IOException {
        String  resource = "SqlMapConfig.xml";
        //如何对SqlMapConfig.xml读取并解析
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建一个会话工厂(SqlSessionFactory),传入mybatis中的配制信息
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取到SqlSession对象,作用是操作数据库
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //关联User.xml文件进行操作
        User user = new User();
        user.setId(32);
        user.setUsername("tom_test123");
        user.setBirthday(new Date());
        user.setSex("0");
        user.setAddress("fffff");
        sqlSession.update("test.updateUser", user);
        sqlSession.commit();
        //释放资源
        sqlSession.close();
    }
}


总结:
parameterType:指定输入参数类型,mybatis 从输入对象中获取参数值拼接在 sql 中。
resultType:指定输出结果类型,mybatis 将 sql 查询结果的一行记录数据映射为 resultType 指定类型的对象。
Sqlsession 的使用范围
SqlSession 中封装了对数据库的操作,如:查询、插入、更新、删除等。

通过 SqlSessionFactory 创建 SqlSession,而 SqlSessionFactory 是通过 SqlSessionFactoryBuilder 进行创建。
1、SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 用于创建 SqlSessionFacoty,SqlSessionFacoty 一旦创建完成就不需要SqlSessionFactoryBuilder 了,因为 SqlSession 是通过 SqlSessionFactory 生产,所以可以将SqlSessionFactoryBuilder 当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。

2、SqlSessionFactory
SqlSessionFactory 是一个接口,接口中定义了 openSession 的不同重载方法,SqlSessionFactory 的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理 SqlSessionFactory。

3、SqlSession
SqlSession 是一个面向用户的接口, sqlSession 中定义了数据库操作,默认使用 DefaultSqlSession 实现类。

使用DAO实现类的方式操作数据库

创建DAO

package com.foreknow.dao;

import java.sql.SQLException;
import java.util.List;



import com.foreknow.bean.User;

public interface UserDao {
    /**
     * 根据id查询用户信息
     * @param id
     * @return User
     * @throws SQLException
     */
    public User findUserById(int id)throws SQLException;
    
    /**
     * 模糊查询用户信息列表
     * @param name
     * @return List<User>
     * @throws SQLException
     */
    public List<User> findUserByName(String name)throws SQLException;
    
    /**
     * 添加用户信息
     * @param user
     * @throws SQLException
     */
    public void insertUser(User user)throws SQLException;
    
    /**
     * 根据id删除用户信息
     * @param id
     * @throws SQLException
     */
    public void deleteUser(int id)throws SQLException;
    
    /**
     * 修改用户信息
     * @param user
     * @throws SQLException
     */
    public void updateUser(User user)throws SQLException;
    
}

创建DAO实现类

package com.foreknow.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.foreknow.bean.User;

public class UserDaoImpl implements UserDao {

// 需要向dao实现类中注入SqlSessionFactory
// 这里通过构造方法注入
private SqlSessionFactory sqlSessionFactory;

public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}

@Override
public User findUserById(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();

User user = sqlSession.selectOne("test.findUserById", id);

// 释放资源
sqlSession.close();

return user;

}

@Override
public List<User> findUserByName(String name) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();

List<User> list = sqlSession.selectList("test.findUserByName", name);

// 释放资源
sqlSession.close();

return list;
}

@Override
public void insertUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();

//执行插入操作
sqlSession.insert("test.insertUser", user);

// 提交事务
sqlSession.commit();

// 释放资源
sqlSession.close();

}

@Override
public void deleteUser(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();

//执行插入操作
sqlSession.delete("test.deleteUser", id);

// 提交事务
sqlSession.commit();

// 释放资源
sqlSession.close();

    }
}

创建JUnit测试类

public class UserDaoImplTest {
private SqlSessionFactory sqlSessionFactory;

// 此方法是在执行testFindUserById之前执行
@Before
public void setUp() throws Exception {
// 创建sqlSessionFactory

// mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream inputStream = Resources.getResourceAsStream(resource);

// 创建会话工厂,传入mybatis的配置文件信息
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
}

@Test
public void testFindUserById() throws Exception {
// 创建UserDao的对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);

// 调用UserDao的方法
User user = userDao.findUserById(1);

System.out.println(user);
      }
}

自定义别名:

在 SqlMapConfig.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"></properties>
    <typeAliases>
        <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
        <package name="com.domain"/>
    </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>
        <package name="com.mapper"></package>
    </mappers>
</configuration>

Mybatis 的 mapper 接口

程序员需要编写mapper接口(相当于Dao接口,增删改查操作)
程序员需要编写 mapper.xml 映射文件,需遵循一些开发规范,mybatis 可以自动生成 mapper 接口类代理对象。
开发规范:
1.在 mapper.xml 中 namespace 等于 mapper 接口地址(所在包名的全路径)
<mapper namespace="com.mapper.UserMapper"></mapper>
2.在 xxxmapper.java 接口中的方法名要与 xxxMapper.xml 中 statement 的 id 一致。
3.在 xxxmapper.java 接口中的输入参数类型要与 xxxMapper.xml 中 statement 的 parameterType 指定的参数类型一致。
4.在 xxxmapper.java 接口中的返回值类型要与 xxxMapper.xml 中 statement 的 resultType 指定的类型一致。
5.接口文件名要与xml映射文件名一致(UserMapper.java和UserMapper.xml)

根据用户 id(主键)查询用户信息
定义Mapper接口
public interface UserMapper {
    public User findUserById(int id);
}

定义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">
<mapper namespace="com.mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="com.domain.User">
        select * from user where id = #{value}
    </select>
</mapper>

主方法测试
    public static void main(String[] args) throws IOException {
        //创建sqlSessionFactory
        //Mybatis 配置文件
        String resource = "SqlMapConfig.xml";
        //得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂,传入Mybatis的配置文件信息
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用UserMapper的方法
        User user = userMapper.findUserById(1);
        System.out.println(user.getUsername());
    }

复合条件查询(动态SQL)

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。

动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

if
choose (when, otherwise)
trim (where, set)
foreach

创建PO类

UserInfo.java

package com.foreknow.bean;

public class UserInfo extends User{
        //可以扩展用户的信息
}

UserQueryInfo.java

public class UserQueryInfo {

//传入多个id
private List<Integer> ids;

//在这里包装所需要的查询条件

//用户查询条件
private UserInfo userInfo;

public UserInfo getUserInfo() {
return userInfo;
}

public void setUserCustom(UserCustom userCustom) {
this.userCustom = userCustom;
}

public List<Integer> getIds() {
return ids;
}

public void setIds(List<Integer> ids) {
this.ids = ids;
}

//可以包装其它的查询条件,订单、商品
//....

}

UserMapper.java

// 用户信息综合查询
public List<UserInfo> findUserList(UserQueryInfo userQueryInfo) throws Exception;

// 用户信息综合查询总数
public int findUserCount(UserQueryInfo userQueryInfo) throws Exception;

UserMapper.xml

<!-- 定义sql片段
id:sql片段的唯 一标识

经验:是基于单表来定义sql片段,这样话这个sql片段可重用性才高
在sql片段中不要包括 where
-->
<sql id="query_user_where">
<if test="userInfo!=null">
<if test="userInfo.sex!=null and userInfo.sex!=''">
and USER .sex = #{userInfo.sex}
</if>
<if test="userInfo.username!=null and userInfo.username!=''">
and USER.username LIKE '%${userInfo.username}%'
</if>
<if test="ids!=null">
<!-- 使用 foreach遍历传入ids
collection:指定输入 对象中集合属性
item:每个遍历生成对象中
open:开始遍历时拼接的串
close:结束遍历时拼接的串
separator:遍历的两个对象中需要拼接的串
-->
<!-- 使用实现下边的sql拼接:
  AND (id=1 OR id=10 OR id=16)
  -->
<foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
<!-- 每个遍历需要拼接的串 -->
id=#{user_id}
</foreach>

<!-- 实现  “ and id IN(1,10,16)”拼接 -->
<!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
每个遍历需要拼接的串
#{user_id}
</foreach> -->

</if>
</if>
</sql>

<!-- 用户信息综合查询
#{userInfo.sex}:取出pojo包装对象中性别值
${userInfo.username}:取出pojo包装对象中用户名称
-->
<select id="findUserList" parameterType="com.foreknow.UserQueryInfo"

resultType="com.foreknow.UserInfo">
SELECT * FROM USER
<!--

where可以自动去掉条件中的第一个and
-->
<where>
<!-- 引用sql片段 的id,如果refid指定的id不在本mapper文件中,需要前边加namespace -->
<include refid="query_user_where"></include>
<!-- 在这里还要引用其它的sql片段  -->
</where>

</select>

<!-- 用户信息综合查询总数
parameterType:指定输入类型和findUserList一样
resultType:输出结果类型
-->
<select id="findUserCount" parameterType="com.foreknow.UserQueryInfo" resultType="int">
  SELECT count(*) FROM USER

  <!--
where可以自动去掉条件中的第一个and
-->
<where>
<!-- 引用sql片段 的id,如果refid指定的id不在本mapper文件中,需要前边加namespace -->
<include refid="query_user_where"></include>
<!-- 在这里还要引用其它的sql片段  -->
</where>

Junit测试

//用户信息的综合 查询
@Test
public void testFindUserList() throws Exception {

SqlSession sqlSession = sqlSessionFactory.openSession();

//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

//创建包装对象,设置查询条件
UserQueryInfo userQueryInfo = new UserQueryInfo();
UserInfo userInfo = new UserInfo();
//由于这里使用动态sql,如果不设置某个值,条件不会拼接在sql中
//userInfo.setSex("1");
userInfo.setUsername("张三");
//传入多个id
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(10);
ids.add(16);
//将ids通过userQueryInfo传入statement中
userQueryInfo.setIds(ids);
userQueryInfo.setUserInfo(userInfo);
//调用userMapper的方法

List<UserInfo> list = userMapper.findUserList(userQueryInfo);
System.out.println(list);

}

Mybatis中的关联查询

数据库表结构

CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '下单用户id',
  `number` varchar(32) NOT NULL COMMENT '订单号',
  `createtime` datetime NOT NULL COMMENT '创建订单时间',
  `note` varchar(100) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `FK_orders_1` (`user_id`),
  CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);
INSERT INTO `orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);
INSERT INTO `orders` VALUES ('5', '10', '1000012', '2015-02-12 16:13:23', null);

数据模型分析思路
1、每张表记录的数据内容
分模块对每张表记录的内容进行熟悉,相当 于你学习系统 需求(功能)的过程。
2、每张表重要的字段设置
非空字段、外键字段
3、数据库级别表与表之间的关系
外键关系
4、表与表之间的业务关系
在分析表与表之间的业务关系时一定要建立 在某个业务意义基础上去分析。

数据模型分析

用户表user:
记录了购买商品的用户信息

订单表:orders
记录了用户所创建的订单(购买商品的订单)

订单明细表:orderdetail:
记录了订单的详细信息即购买商品的信息

商品表:items
记录了商品信息

表与表之间的业务关系:
在分析表与表之间的业务关系时需要建立 在某个业务意义基础上去分析。
先分析数据级别之间有关系的表之间的业务关系:

usre和orders:
user---->orders:一个用户可以创建多个订单,一对多
orders--->user:一个订单只由一个用户创建,一对一

orders和orderdetail:
orders--->orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系

orderdetail--> orders:一个订单明细只能包括在一个订单中,一对一

orderdetail和itesm:
orderdetail--->itesms:一个订单明细只对应一个商品信息,一对一

items--> orderdetail:一个商品可以包括在多个订单明细 ,一对多

再分析数据库级别没有关系的表之间是否有业务关系:
orders和items:
orders和items之间可以通过orderdetail表建立 关系。

创建JavaBean模型关系
Orders模型

public class Orders {
    private Integer id;

    private Integer userId;

    private String orderid;

    private Date createtime;

    private String note;

    // 用户信息(用于resultMap)
    private User user;

    //Orders => Orderdetail 为一对多的关系
    //最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中。
    private List<Orderdetail> orderdetails;

    public List<Orderdetail> getOrderdetails() {
        return orderdetails;
    }

    public void setOrderdetails(List<Orderdetail> orderdetails) {
        this.orderdetails = orderdetails;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getOrderid() {
        return orderid;
    }

    public void setOrderid(String orderid) {
        this.orderid = orderid;
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

User模型

User.java
//一个用户可以有多个订单(建立用户与商品之间的关系)
//用户创建的订单列表
private List<Orders> ordersList;
public List<Orders> getOrdersList() {
return ordersList;
}

public void setOrdersList(List<Orders> ordersList) {
this.ordersList = ordersList;
}

Items模型

public class Items {
    private Integer id;

    private String name;

    private Float price;

    private String pic;

    private Date createtime;

    private String detail;

    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 == null ? null : name.trim();
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic == null ? null : pic.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail == null ? null : detail.trim();
    }

    @Override
    public String toString() {
    return "Items [id=" + id + ", name=" + name + ", price=" + price
    + ", pic=" + pic + ", createtime=" + createtime + ", detail="
    + detail + "]";
    }
}

Orderdetail模型

public class Orderdetail {
    private Integer id;

    private Integer ordersId;

    private Integer itemsId;

    private Integer itemsNum;

    //明细对应的商品信息
    private Items items;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getOrdersId() {
        return ordersId;
    }

    public void setOrdersId(Integer ordersId) {
        this.ordersId = ordersId;
    }

    public Integer getItemsId() {
        return itemsId;
    }

    public void setItemsId(Integer itemsId) {
        this.itemsId = itemsId;
    }

    public Integer getItemsNum() {
        return itemsNum;
    }

    public void setItemsNum(Integer itemsNum) {
        this.itemsNum = itemsNum;
    }

    public Items getItems() {
        return items;
   }

    public void setItems(Items items) {
        this.items = items;
   }

    @Override
    public String toString() {
        return "Orderdetail [id=" + id + ", ordersId=" + ordersId
      + ", itemsId=" + itemsId + ", itemsNum=" + itemsNum + "]";
    }
}

创建UserMapper接口

public interface UserMapper {
     // 查询订单关联查询用户使用resultMap
    public List<Orders> findOrdersUserResultMap() throws Exception;

    // 查询订单(关联用户)以及订单明细
    public List<Orders> findOrdersAndOrderDetailResultMap() throws Exception;

    // 查询用户购买商品信息
    public List<User> findUserAndItemsResultMap() throws Exception;
}

创建UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPEmapper  PUBLIC"-//mybatis.org//DTD Mapper 3.0//EN"  "[http://mybatis.org/dtd/mybatis-3-mapper.dtd](http://mybatis.org/dtd/mybatis-3-mapper.dtd)">

<!-- 订单查询关联用户的resultMap
将整个查询的结果映射到com.foreknow.Orders中
-->
<resultMap type="com.foreknow.Orders" id="OrdersUserResultMap">
<!-- 配置映射的订单信息 -->
<!-- id:指定查询列中的唯 一标识,订单信息的中的唯 一标识,如果有多个列组成唯一标识,配置多个id
column:订单信息的唯 一标识 列
property:订单信息的唯 一标识 列所映射到Orders中哪个属性
  -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<!-- <result column="orderid" property="orderid"/> -->
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>

<!-- 配置映射的关联的用户信息 -->
<!-- association:用于映射关联查询单个对象的信息
property:要将关联查询的用户信息映射到Orders中哪个属性
-->
<association property="user"  javaType="com.foreknow.User">
<!-- id:关联查询用户的唯 一标识
column:指定唯 一标识用户信息的列
javaType:映射到user的哪个属性
-->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>

<!-- 订单及订单明细的resultMap
使用extends继承,不用在中配置订单信息和用户信息的映射
-->
<resultMap id="OrdersAndOrderDetailResultMap" type="com.foreknow.Orders" extends="OrdersUserResultMap">
<!-- 订单信息 -->
<!-- 用户信息 -->
<!-- 使用extends继承,不用在中配置订单信息和用户信息的映射 -->

<!-- 订单明细信息
一个订单关联查询出了多条明细,要使用collection进行映射
collection:对关联查询到多条记录映射到集合对象中
property:将关联查询到多条记录映射到Orders哪个属性
ofType:指定映射到list集合属性中pojo的类型
-->

<collection property="orderdetails" ofType="com.foreknow.Orderdetail">

<!-- id:订单明细唯 一标识
property:要将订单明细的唯 一标识 映射到com.foreknow.Orderdetail的哪个属性
  -->
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
</collection>

</resultMap>

<!-- 查询用户及购买的商品 -->
<resultMap type="com.foreknow.User" id="UserAndItemsResultMap">
<!-- 用户信息 -->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>

<!-- 订单信息
一个用户对应多个订单,使用collection映射
-->
<collection property="ordersList" ofType="com.foreknow.Orders">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="orderid" property="orderid"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>

<!-- 订单明细
        一个订单包括 多个明细
         -->
<collection property="orderdetails" ofType="com.foreknow.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
<!-- 商品信息
         一个订单明细对应一个商品
          -->
<association property="items" javaType="com.foreknow.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
<result column="items_price" property="price"/>
</association>
</collection>
</collection>

</resultMap>

<!-- 查询订单关联查询用户信息,使用resultmap -->
<select id="findOrdersUserResultMap" resultMap="OrdersUserResultMap"> 
SELECT
orders.*,
USER.username,
USER.sex,
USER.address
FROM
orders,
USER
WHERE orders.user_id = user.id
</select>

<selectid="findOrdersAndOrderDetailResultMap" resultMap="OrdersAndOrderDetailResultMap"> 
SELECT
orders.*,
USER.username,
USER.sex,
USER.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id
FROM
orders,
USER,
orderdetail
WHERE orders.user_id=user.id and orders.id=orderdetail.orders_id 
</select>

<selectid="findUserAndItemsResultMap" resultMap="UserAndItemsResultMap"> 
SELECT
orders.*,
USER.username,
USER.sex,
USER.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.name items_name,
items.detail items_detail,
items.price items_price
from
orders,
user,
orderdetail,
items
where orders.user_id=user.id and orders.id=orderdetail.orders_id and orderdetail.items_id=items.id 
</select>
</mapper>
JUnit测试
public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    // 此方法是在执行testFindUserById之前执行
    @Before
    public void setUp() throws Exception {
        // 创建sqlSessionFactory

        // mybatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 创建会话工厂,传入mybatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testFindOrdersUserResultMap() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 调用maper的方法
        List<Orders> list = userMapper.findOrdersUserResultMap();

        System.out.println(list);

        sqlSession.close();
    }

    @Test
    public void testFindOrdersAndOrderDetailResultMap() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 调用maper的方法
        List<Orders> list = userMapper.findOrdersAndOrderDetailResultMap();

        System.out.println(list);

        sqlSession.close();
    }

    @Test
    public void testFindUserAndItemsResultMap() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);       
       // 调用maper的方法
        List<User> list = userMapper.findUserAndItemsResultMap();

        System.out.println(list);

        sqlSession.close();
    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容