image.png

image.png

Mybatis

01.mybatis介绍

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码

Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatement、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

02.jdbc问题总结如下:

1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

2、 Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

3、 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。

4、 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。


03.Mybatis架构

image.png

1、 mybatis配置

        SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。

        mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。

2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂

3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。

4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sqlid即是Mapped statementid

6、 Mapped Statementsql执行输入参数进行定义,包括HashMap、基本类型、pojoExecutor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。

7、 Mapped Statementsql执行输出结果进行定义,包括HashMap、基本类型、pojoExecutor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

04.入门程序

SqlMapConfig.xml

    配置数据库连接池,引入SQL映射文件。

<?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>
    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事务管理-->
        <transactionManager type="JDBC" />
        <!-- 数据库连接池-->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://192.168.142.11:3306/mybatis?characterEncoding=utf-8"/>
            <property name="username" value="mycon"/>
            <property name="password" value="123"/>
        </dataSource>
        </environment>
    </environments>
    <mappers>
    <mapper resource="User.xml"/>
    </mappers>

</configuration>
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">
 
    <!-- 
    id:sql语句唯一标识
    parameterType:指定传入参数类型
    resultType:返回结果集类型
    #{}占位符:起到占位作用,如果传入的是基本类型(string,long,double,int,boolean,float等),那么#{}中的变量名称可以随意写.
     -->
    <select id="findUserById" parameterType="java.lang.Integer" resultType="com.my.pojo.User">
        select * from user where id=#{id}
    </select>
    
    <!-- 
    如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所以映射文件中应该配置成集合泛型的类型
    ${}拼接符:字符串原样拼接,如果传入的参数是基本类型(string,long,double,int,boolean,float等),那么${}中的变量名称必须是value
    注意:拼接符有sql注入的风险,所以慎重使用
     -->
    <select id="findUserByUserName" parameterType="java.lang.String" resultType="com.my.pojo.User">
        select * from user where username like '%${value}%'
    </select>
    
    <!-- 
    #{}:如果传入的是pojo类型,那么#{}中的变量名称必须是pojo中对应的属性.属性.属性.....
    如果要返回数据库自增主键:可以使用select LAST_INSERT_ID()
     -->
    <insert id="insertUser" parameterType="com.my.pojo.User" >
        <!-- 执行 select LAST_INSERT_ID()数据库函数,返回自增的主键
        keyProperty:将返回的主键放入传入参数的Id中保存.
        order:当前函数相对于insert语句的执行顺序,在insert前执行是before,在insert后执行是AFTER
        resultType:id的类型,也就是keyproperties中属性的类型
        -->
        
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
        insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>
    <!-- 需要增加通过select uuid()得到uuid值
    <insert  id="insertUser" parameterType="cn.itcast.mybatis.po.User">
        <selectKey resultType="java.lang.String" order="BEFORE" keyProperty="id">
            select uuid()
        </selectKey>
        insert into user(id,username,birthday,sex,address) 
         values(#{id},#{username},#{birthday},#{sex},#{address})
    </insert>
    注意这里使用的order是“BEFORE” -->
    
    <delete id="delUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>
    
    <update id="updateUserById" parameterType="com.my.pojo.User">
        update user set username=#{username} where id=#{id}
    </update>

</mapper>

UserTest.java
public class UserTest {
    @Test
    public void testFindUserById() throws Exception{
        String resource = "SqlMapConfig.xml";
        //通过流将核心配置文件读取进来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过核心配置文件输入流来创建会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂创建会话
        SqlSession openSession = factory.openSession();
        //第一个参数:所调用的sql语句= namespace+.+sql的ID
        User user = openSession.selectOne("test.findUserById", 1);
        System.out.println(user);
        openSession.close();
    }
    
    @Test
    public void testFindUserbyUserName() throws Exception{
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession openSession = factory.openSession();
        
        List<User> list = openSession.selectList("test.findUserByUserName", "王");
        System.out.println(list);
        openSession.close();
    }
    
    @Test
    public void testInsertUser() throws Exception{
        String resource = "SqlMapConfig.xml";
        //通过流将核心配置文件读取进来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过核心配置文件输入流来创建会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂创建会话
        SqlSession openSession = factory.openSession();
        
        User user = new User();
        user.setUsername("赵四");
        user.setBirthday(new Date());
        user.setSex("1");
        user.setAddress("北京昌平");
        System.out.println("====" + user.getId());
        
        openSession.insert("test.insertUser", user);
        //提交事务(mybatis会自动开启事务,但是它不知道何时提交,所以需要手动提交事务)
        openSession.commit();
        openSession.close();
        System.out.println("====" + user.getId());
    }
    
    @Test
    public void testDelUserById()throws Exception{
        String resource = "SqlMapConfig.xml";
        //通过流将核心配置文件读取进来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过核心配置文件输入流来创建会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂创建会话
        SqlSession openSession = factory.openSession();
        
        openSession.delete("test.delUserById", 29);
        //提交
        openSession.commit();
    }
    
    @Test
    public void testUpdateUserById() throws Exception{
        String resource = "SqlMapConfig.xml";
        //通过流将核心配置文件读取进来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过核心配置文件输入流来创建会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂创建会话
        SqlSession openSession = factory.openSession();
        
        User user = new User();
        user.setId(28);
        user.setUsername("王麻子");
        openSession.update("test.updateUserById", user);
        
        //提交
        openSession.commit();
    }
}

05.原生dao

SqlMapConfig.xml
User.xml
UserDao\UserDaoImpl
public interface UserDao {
    public User findUserById(Integer id);
    public List<User> findUserByUserName(String userName);
}
public class UserDaoImpl implements UserDao {
 
    private SqlSessionFactory sqlSessionFactory;
        //通过构造方法注入
        public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }
     
    @Override
    public User findUserById(Integer id) {
        //SqlSession是线程不安全的,所以要在方法体内
        SqlSession openSession = sqlSessionFactory.openSession();
        User user = openSession.selectOne("test.findUserById", id);
        openSession.close();
        return user;
    }
     
    @Override
    public List<User> findUserByUserName(String userName) {
        SqlSession openSession = sqlSessionFactory.openSession();
        List<User> list = openSession.selectList("test.findUserByUserName", userName);
        openSession.close();
        return list;
    }
}
UserDaoTest
public class UserDaoTest {
    private SqlSessionFactory factory;
    @Before
    public void setUp()throws Exception{
        String resource = "SqlMapConfig.xml";
        //通过流将核心配置文件读取进来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过核心配置文件输入流来创建会话工厂
        factory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testFindUserById()throws Exception{
        UserDao userDao = new UserDaoImpl(factory);
        User user = userDao.findUserById(1);
        System.out.println(user);
    }
    @Test
    public void findUserByUserName()throws Exception{
        UserDao userDao = new UserDaoImpl(factory);
        List<User> listuser = userDao.findUserByUserName("小明");
        System.out.println(listuser);
    }
}

06.mapper代理

image.png

SqlMapConfig.xml扫描
<mappers>
    <!-- 
    使用class属性引入接口的全路径名称:
    使用规则:
        1. 接口的名称和映射文件名称除扩展名外要完全相同
        2. 接口和映射文件要放在同一个目录下
     -->
    <!-- <mapper class="com.my.mapper.UserMapper"/> -->
    
    <!-- 使用包扫描的方式批量引入Mapper接口 
        使用规则:
        1. 接口的名称和映射文件名称除扩展名外要完全相同
        2. 接口和映射文件要放在同一个目录下
    -->
    <package name="com.my.mapper"/>

</mappers>
UserMapper
public interface UserMapper {
    public User findUserById(Integer id);
    public List<User> findUserByUserName(String userName);
    public void insertUser(User user);
}
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接口代理实现编写规则:
1. 映射文件中namespace要等于接口的全路径名称
2. 映射文件中sql语句id要等于接口的方法名称,(接口=方法名称)
3. 映射文件中传入参数类型要等于接口方法的传入参数类型,(参数相同)
4. 映射文件中返回结果集类型要等于接口方法的返回值类型
 -->
<mapper namespace="com.my.mapper.UserMapper">
 
    <select id="findUserById" parameterType="int" resultType="com.my.pojo.User">
        select * from user where id=#{id}
    </select>
     
    <select id="findUserByUserName" parameterType="String" resultType="com.my.pojo.User">
        select * from user where username like '%${value}%'
    </select>
     
    <!-- 
    #{}:如果传入的是pojo类型,那么#{}中的变量名称必须是pojo中对应的属性.属性.属性.....
    如果要返回数据库自增主键:可以使用select LAST_INSERT_ID()
     -->
    <insert id="insertUser" parameterType="com.my.pojo.User" >
        <!-- 执行 select LAST_INSERT_ID()数据库函数,返回自增的主键
        keyProperty:将返回的主键放入传入参数的Id中保存.
        order:当前函数相对于insert语句的执行顺序,在insert前执行是before,在insert后执行是AFTER
        resultType:id的类型,也就是keyproperties中属性的类型
        -->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
        insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>
</mapper>
UserMapperTest
public class UserMapperTest {
    private SqlSessionFactory factory;
     
    @Before
    public void setUp() throws Exception {
        String resource = "SqlMapConfig.xml";
        // 通过流将核心配置文件读取进来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 通过核心配置文件输入流来创建会话工厂
        factory = new SqlSessionFactoryBuilder().build(inputStream);
    
    }
     
    //动态代理形式中,如果返回 结果集为list,那么mybatis会在生成实现类的时候会自动调用selectlist方法
    @Test
    public void testFindUserById() throws Exception {
        SqlSession openSession = factory.openSession();
        // 通过getMapper方法来实例化接口
        UserMapper mapper = openSession.getMapper(UserMapper.class);
         
        User user = mapper.findUserById(1);
        openSession.close();
        System.out.println(user);
    }
    
    @Test
    public void testFindByUserName()throws Exception{
        SqlSession openSession = factory.openSession();
        // 通过getMapper方法来实例化接口
        UserMapper mapper = openSession.getMapper(UserMapper.class);
        List<User> listUser = mapper.findUserByUserName("小雨");
        openSession.close();
        System.out.println(listUser);
    }
    @Test
    public void testInsert()throws Exception{
        SqlSession openSession = factory.openSession();
        // 通过getMapper方法来实例化接口
        UserMapper mapper = openSession.getMapper(UserMapper.class);
        User user = new User();
        user.setAddress("昌平");
        user.setSex("2");
        user.setUsername("小雨");
        mapper.insertUser(user);
        System.out.println(user.getId());
        //提交事务(mybatis会自动开启事务,但是它不知道何时提交,所以需要手动提交事务)
        openSession.commit();
        openSession.close();
    }
}

07.总结

pojo:不按mvc分层,只是java bean有一些属性,还有get set方法

domain:不按mvc分层,只是java bean有一些属性,还有get set方法

po:用在持久层,还可以再增加或者修改的时候,从页面直接传入action,它里面的java bean 类名等于表名,

属性名等于表的字段名,还有对应的get set方法

vo: view object表现层对象,主要用于在高级查询中从页面接收传过来的各种参数.好处是扩展性强

bo: 用在servie,现在企业基本不用.

这些po,vo, bo,pojo可以用在各种层面吗

可以,也就是po用在表现层,vo用在持久层不报错,因为都是普通的java bean没有语法错误.

hibernate和mybatis区别:

hibernate:它是一个标准的orm框架,比较重量级,学习成本高.

    优点:高度封装,使用起来不用写sql,开发的时候,会减低开发周期.

    缺点:sql语句无法优化

    应用场景:oa(办公自动化系统), erp(企业的流程系统),还有一些政府项目,

    总的来说,在用于量不大,并发量小的时候使用.

    mybatis:它不是一个orm框架, 它是对jdbc的轻量级封装, 学习成本低,比较简单

    有点:学习成本低, sql语句可以优化, 执行效率高,速度快

    缺点:编码量较大,会拖慢开发周期

    应用场景: 互联网项目,比如电商,P2p

         总的来说是用户量较大,并发高的项目.

08.输入输出映射

<!-- 
    mapper接口代理实现编写规则:
    1. 映射文件中namespace要等于接口的全路径名称
    2. 映射文件中sql语句id要等于接口的方法名称
    3. 映射文件中传入参数类型要等于接口方法的传入参数类型
    4. 映射文件中返回结果集类型要等于接口方法的返回值类型
 -->
<mapper namespace="cn.itheima.mapper.UserMapper">

    <!-- 
        id:sql语句唯一标识
        parameterType:指定传入参数类型
        resultType:返回结果集类型
        #{}占位符:起到占位作用,如果传入的是基本类型(string,long,double,int,boolean,float等),那么#{}中的变量名称可以随意写.
     -->
    <select id="findUserById" parameterType="int" resultType="cn.itheima.pojo.User">
        select * from user where id=#{id}
    </select>
    
    <!-- 
        如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所以映射文件中应该配置成集合泛型的类型
        ${}拼接符:字符串原样拼接,如果传入的参数是基本类型(string,long,double,int,boolean,float等),那么${}中的变量名称必须是value
        注意:拼接符有sql注入的风险,所以慎重使用
     -->
    <select id="findUserByUserName" parameterType="string" resultType="user">
        select * from user where username like '%${value}%'
    </select>
    
    <!-- 
        #{}:如果传入的是pojo类型,那么#{}中的变量名称必须是pojo中对应的属性.属性.属性.....
        如果要返回数据库自增主键:可以使用select LAST_INSERT_ID()
     -->
    <insert id="insertUser" parameterType="cn.itheima.pojo.User" >
        <!-- 执行 select LAST_INSERT_ID()数据库函数,返回自增的主键
        keyProperty:将返回的主键放入传入参数的Id中保存.
        order:当前函数相对于insert语句的执行顺序,在insert前执行是before,在insert后执行是AFTER
        resultType:id的类型,也就是keyproperties中属性的类型
        -->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
        insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>
    
    <select id="findUserbyVo" parameterType="cn.itheima.pojo.QueryVo" resultType="cn.itheima.pojo.User">
        select * from user where username like '%${user.username}%' and sex=#{user.sex}
    </select>
    
    <!-- 只有返回结果为一行一列的时候,那么返回值类型才可以指定成基本类型 -->
    <select id="findUserCount" resultType="java.lang.Integer">
        select count(*) from user 
    </select>

</mapper>

09.动态sql

If,where
<!-- 封装sql条件,封装后可以重用. 
id:是这个sql条件的唯一标识 -->
<sql id="user_Where">
    <!-- where标签作用:
    会自动向sql语句中添加where关键字
    会去掉第一个条件的and关键字
     -->
    <where>
        <if test="username != null and username != ''">
            and username like '%${username}%'
        </if>
        <if test="sex != null and sex != ''">
            and sex=#{sex}
        </if>
    </where>
</sql>

<select id="findUserByUserNameAndSex" parameterType="cn.itheima.pojo.User" resultType="cn.itheima.pojo.User">
    select * from user 
    
    <!-- 调用sql条件 -->
    <include refid="user_Where"></include>
</select>
Foreach
public class QueryVo {
 
private User user;

private List<Integer> ids;
<select id="findUserByIds" parameterType="cn.itheima.pojo.QueryVo" resultType="cn.itheima.pojo.User">
    select * from user
    
    select * from user
    <where>
        <if test="ids != null">
            <!-- 
            foreach:循环传入的集合参数
            collection:传入的集合的变量名称
            item:每次循环将循环出的数据放入这个变量中
            open:循环开始拼接的字符串
            close:循环结束拼接的字符串
            separator:循环中拼接的分隔符
             -->
            <foreach collection="ids" item="id" open="id in (" close=")" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
</select>
public void testFindUserbyIds() throws Exception{
    SqlSession openSession = factory.openSession();
    //通过getMapper方法来实例化接口
    UserMapper mapper = openSession.getMapper(UserMapper.class);
    
    QueryVo vo = new QueryVo();
    List<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(16);
    ids.add(28);
    ids.add(22);
    vo.setIds(ids);
    
    List<User> list = mapper.findUserByIds(vo);
    System.out.println(list);
}

010.单个对象映射关系

一对一自动映射(少用)
public class CustomOrders extends Orders{
     
    private int uid;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
}


//================================================

<!-- 一对一:自动映射 -->
<select id="findOrdersAndUser1" resultType="cn.itheima.pojo.CustomOrders">
    select a.*, b.id uid, username, birthday, sex, address 
    from orders a, user b 
    where a.user_id = b.id
</select>
一对一手动映射
public class Orders {
    private Integer id;
 
    private Integer userId;
 
    private String number;
 
    private Date createtime;
 
    private String note;
    
    private User user;
<!-- 一对一:手动映射 -->
<!-- 
id:resultMap的唯一标识
type:将查询出的数据放入这个指定的对象中
注意:手动映射需要指定数据库中表的字段名与java中pojo类的属性名称的对应关系
 -->
<resultMap type="cn.itheima.pojo.Orders" id="orderAndUserResultMap">
    <!-- id标签指定主键字段对应关系
    column:列,数据库中的字段名称
    property:属性,java中pojo中的属性名称
     -->
    <id column="id" property="id"/>
    
    <!-- result:标签指定非主键字段的对应关系 -->
    <result column="user_id" property="userId"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>
    
    <!-- 这个标签指定单个对象的对应关系 
    property:指定将数据放入Orders中的user属性中
    javaType:user属性的类型
    -->
    <association property="user" javaType="cn.itheima.pojo.User">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
    </association>
</resultMap>
<select id="findOrdersAndUser2" resultMap="orderAndUserResultMap">
    select a.*, b.id uid, username, birthday, sex, address 
    from orders a, user b 
    where a.user_id = b.id
</select>
@Test
public void testFindOrdersAnduUser2() throws Exception{
    SqlSession openSession = factory.openSession();
    //通过getMapper方法来实例化接口
    UserMapper mapper = openSession.getMapper(UserMapper.class);
    
    List<Orders> list = mapper.findOrdersAndUser2();
    System.out.println(list);
}

011.集合对象映射

public class User {
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
    
    private List<Orders> ordersList;
<resultMap type="cn.itheima.pojo.User" id="userAndOrdersResultMap">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="birthday" property="birthday"/>
    <result column="sex" property="sex"/>
    <result column="address" property="address"/>
    
    <!-- 指定对应的集合对象关系映射
    property:将数据放入User对象中的ordersList属性中
    ofType:指定ordersList属性的泛型类型
     -->
    <collection property="ordersList" ofType="cn.itheima.pojo.Orders">
        <id column="oid" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
    </collection>
</resultMap>
<select id="findUserAndOrders" resultMap="userAndOrdersResultMap">
    select a.*, b.id oid ,user_id, number, createtime 
    from user a, orders b where a.id = b.user_id
</select>
public void testFindUserAndOrders() throws Exception{
    SqlSession openSession = factory.openSession();
    //通过getMapper方法来实例化接口
    UserMapper mapper = openSession.getMapper(UserMapper.class);
    
    List<User> list = mapper.findUserAndOrders();
    System.out.println(list);
}

012.springmybatis原生dao整合

applicationContext.xml

:将回话工厂放在spring中配置

<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="10" />
    <property name="maxIdle" value="5" />
</bean>

<!-- 整合Sql会话工厂归spring管理 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 指定mybatis核心配置文件 -->
    <property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
    <!-- 指定会话工厂使用的数据源 -->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 
    配置原生Dao实现   
    注意:class必须指定Dao实现类的全路径名称
-->
<bean id="userDao" class="cn.itheima.dao.UserDaoImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
Dao层处理:

注意需要继承SqlSessionDaoSupport,可以用于获取会化工厂从而得到连接

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
    @Override
    public User findUserById(Integer id) {
        //sqlSesion是线程不安全的,所以它的最佳使用范围在方法体内
        SqlSession openSession = this.getSqlSession();
        User user = openSession.selectOne("test.findUserById", id);
        //整合后会话归spring管理,所以不需要手动关闭.
        //openSession.close();
        return user;
    }
     
    @Override
    public List<User> findUserByUserName(String userName) {
        SqlSession openSession = this.getSqlSession();
        List<User> list = openSession.selectList("test.findUserByUserName", userName);
        return list;
    }
}
测试类:
public class UserDaoTest {
 
    private ApplicationContext applicatonContext;
    @Before
    public void setUp() throws Exception{
        String configLocation = "classpath:ApplicationContext.xml";
        applicatonContext = new ClassPathXmlApplicationContext(configLocation);
    }
    @Test
    public void testFindUserById() throws Exception{
        //获取UserDao对象, getBean中的字符串是在ApplicationContext.xml中声明的
        UserDao userDao = (UserDao)applicatonContext.getBean("userDao");
        User user = userDao.findUserById(1);
        System.out.println(user);
    }
}

013.springmybatismapper整合

applicationContext.xml
<!-- Mapper接口代理实现 -->
<!--  <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> -->
<!-- 配置mapper接口的全路径名称 -->
<!--   <property name="mapperInterface" value="cn.itheima.mapper.UserMapper"></property> -->
<!--   <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> -->
<!--  </bean> -->

<!-- 使用包扫描的方式批量引入Mapper
    扫描后引用的时候可以使用类名,首字母小写.
 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 指定要扫描的包的全路径名称,如果有多个包用英文状态下的逗号分隔 -->
    <property name="basePackage" value="cn.itheima.mapper"></property>
</bean>

注意:在SqlMapConfig.xml中就不需要引入mapper配置文件了

测试
public class UserMapperTest {
    private ApplicationContext applicatonContext;
    
    @Before
    public void setUp() throws Exception{
        String configLocation = "classpath:ApplicationContext.xml";
        applicatonContext = new ClassPathXmlApplicationContext(configLocation);
    }
    
    @Test
    public void  testFindUserById() throws Exception{
        UserMapper userMapper = (UserMapper)applicatonContext.getBean("userMapper");
        
        User user = userMapper.findUserById(1);
        System.out.println(user);
    }
}

014.逆向工程

01.搭建步骤

01.Mybatis核心包,连接数据库的包

image.png

02.创建generator.xml(不用记具体的,只要会改其中的一些内容即可)

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
            password="admin">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
        connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
        userId="yycg"
        password="yycg">
        </jdbcConnection> -->
         
        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
        NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
         
        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="cn.itheima.pojo"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
                <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="cn.itheima.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.itheima.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table tableName="orders"></table>
        <table tableName="user"></table>
        
        <!-- 有些表的字段需要指定java类型
        <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
</generatorConfiguration>

需要注意的点:数据库连接、生成mapperpojo的位置、指定数据库表

 

03.运行方法

import java.io.File;
import java.util.ArrayList;
import java.util.List;
 
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
 
public class StartServer {
    
    public void generator() throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("genarator.xml"); 
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback, warnings);
        myBatisGenerator.generate(null);
    }
    
    public static void main(String[] args) throws Exception {
        try {
            StartServer startServer = new StartServer();
            startServer.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
注意:

逆向工程以后就会生成mapper配置文件里面会有可能执行的sql集合,直接拿过来用就好

逆向工程生成mapper文件里面的查询都是单表查询,如果想要多表查询,则在mapper配置文件最后再写即可。

015.逆向工程生成单表操作总结

public class UserMapperTest {
    private ApplicationContext applicatonContext;
    
    @Before
    public void setUp() throws Exception{
        String configLocation = "classpath:ApplicationContext.xml";
        applicatonContext = new ClassPathXmlApplicationContext(configLocation);
    }
}
01.根据主键ID查询
@Test
public void testFindUserById() throws Exception{
    UserMapper userMapper = (UserMapper)applicatonContext.getBean("userMapper");
    
    User user = userMapper.selectByPrimaryKey(1);
    System.out.println(user);
}

02.根据性别和名称查询

@Test
public void testFindUserAndSex() throws Exception{
    UserMapper userMapper = (UserMapper)applicatonContext.getBean("userMapper");
    
    //创建UserExample对象
    UserExample userExample = new UserExample();
    //通过UserExample对象创建查询条件封装对象(Criteria中是封装的查询条件)
    Criteria createCriteria = userExample.createCriteria();
    
    //加入查询条件
    createCriteria.andUsernameLike("%王%");
    createCriteria.andSexEqualTo("1");
    
    List<User> list = userMapper.selectByExample(userExample);
    System.out.println(list);
}
/*
思路:
建立mapper对象userMapper-->
userMapper执行方法,alt+/出现selectByExample方法-->
selectByExample方法里面的参数是UserExample -->
建立UserExample 对象,并建立Criteria 对象,即参数对象-->
添加参数*/