MyBatis框架

1 - MyBatis框架介绍

什么是框架:

  框架是一个框架,半成品的软件,定义好了一些基础功能,需要加入你的功能就是完整的,基础功能是可重复使用的,可升级的软件

框架的特点:

1.框架一般不是全能的,不能做所有事情

2.框架是针对某一个领域有效。特长在某一个方面,比如mybatis做数据库操作强,但是其他的不能做

3.框架是一个软件

 

使用Jdbc的缺陷:

  1.代码比较多,开发效率低

  2.需要关注connection,statement,result对象创建个销毁

  3.重复的代码比较多些

  4.对result查询的结果,需要自己封装为list

  5.业务代码和数据库的操作混在一起

 

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

2 - MyBatis下载与安装

下载地址:https://github.com/mybatis/mybatis-3/releases

 

 

如上图,第一个为MyBatis框架压缩包,第二个和第三个分别为Windows何Linux系统下的源代码,如果只需使用下载第一个即可

最后解压文件夹

3 - MyBatis框架搭建

准备工作(创建表和基础数据):

1)创建项目,并且在创建文件夹:WEB-INF/lib 搭建包结构(WEB-INF/lib/相关jar包)

 

2)导入mybatis相关的jar包和mysql驱动包,导入log4j(D:\MyBatis\mybatis-3.5.5\lib\log4j-1.2.17.jar)相关jar包

 

3)在src根下创建mybatis注配置文件mybatis-config.xml和日志文件log4j.properties,搭建配置文件结构和日志文件(文档第3页)

src/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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/> <!--事务管理-->
            <dataSource type="POOLED"> <!--数据源,连接池等-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/springdb?useSSL=FALSE&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true"/>
                <property name="username" value="root"/>
                <property name="password" value="101323"/>
            </dataSource>
        </environment>
        <!--
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
            </dataSource>
         </environment>
        -->
    </environments>
    <mappers>
        <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <mapper resource="com/howie/mapper/ProvinceMapper.xml"/>
    </mappers>
</configuration>
mybatis-config.xml

src/log4j.properties    文件内容如下

# Global logging configuration
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration ...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 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
log4j.properties

 

4)创建mapper包结构,创建SQL映射文件XxxMapper.xml(文档第4页),这里以province数据表为例

src/com/howie/mapper/ProvinceMapper.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:命名空间
        不同mapper映射文件使用namespace来区分
        不同的mapper映射文件所使用的namespace的命名不允许出现重复
    使用命名空间.sqlId的形式来找到我们想要执行的sql语句。例如 province.getById

-->
<mapper namespace="province">
    <!--<select id="getById"></select>-->
    <!--
        sql语句必须写在相应的标签中,标签有update,delete,insert,select
        <insert>:在标签对中写insert开头的SQL语句,处理添加操作
        <update>:在标签对中写update开头的SQL语句,处理更新操作
        <delete>:在标签对中写delete开头的SQL语句,处理添删除作
        <select>:在标签对中写select开头的SQL语句,处理查询操作

        属性parameterType:为SQL语句传递的参数的类型
        属性resultType:SQL语句执行完后返回的数据类型
    -->
    <select id="getById" parameterType="java.lang.Integer" resultType="com.howie.domain.Province">
        select * from province where id = #{id}
    </select>
</mapper>
ProvinceMapper.xml

 

5)搭建测试包与测试类,测试根据id查单条,模板在文档第2页

 src/com/howie/test/Test1.java

package com.howie.test;
import com.howie.domain.Province;
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 java.io.InputStream;

public class Test1 {
    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        // 输出流
        InputStream inputStream = null;
        try {
            // 通过加载MyBatis的主配置文件mybatis-config.xml,创建输入流对象
            inputStream = Resources.getResourceAsStream(resource);
        } catch(Exception e){
            e.printStackTrace();
        }
        /*
        SqlSessionFactoryBuilder:SqlSessionFactory的建造者。通过该建造者对象调用建造方法,为我们创建一个SqlSessionFactory对象

        sqlSessionFactory对象唯一的作用就是为我们创建SqlSession对象我们未来所有的操作,使用的都是SqlSession对象
         */
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession(); // Java与数据库之间的会话
        // 我们未来所有的操作(例如:增删改查,处理事务等),使用的都是SqlSession对象

        /*
        需求:根据id查询单条数据
            如果取得的是单条记录,我们调用selectOne方法
            参数1:根据命名空间.sqlId的形式找到我们需要使用的SQL语句
            参数2:我们要为SQL语句中传递的参数
         */
        Province province = session.selectOne("province.getById", 1);
        System.out.println(province);
        session.close();
    }
}
Test1.java

4 - MyBatis完成基本的增删改查操作

基于上面的搭建,我们可以在MyBatis中完成基础的操作,这里的操作都是在测试类中完成

src/com/howie/mapper/ProvinceMapper.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:命名空间
        不同mapper映射文件使用namespace来区分
        不同的mapper映射文件所使用的namespace的命名不允许出现重复
    使用命名空间.sqlId的形式来找到我们想要执行的sql语句。例如 province.getById

-->
<mapper namespace="province">
    <!--<select id="getById"></select>-->
    <!--
        sql语句必须写在相应的标签中,标签有update,delete,insert,select
        <insert>:在标签对中写insert开头的SQL语句,处理添加操作
        <update>:在标签对中写update开头的SQL语句,处理更新操作
        <delete>:在标签对中写delete开头的SQL语句,处理添删除作
        <select>:在标签对中写select开头的SQL语句,处理查询操作

        属性parameterType:为SQL语句传递的参数的类型
        属性resultType:SQL语句执行完后返回的数据类型
    -->
    <!--1.根据id获取省份信息-->
    <select id="getById" parameterType="java.lang.Integer" resultType="com.howie.domain.Province">
        select * from province where id = #{id}
    </select>
    <!--2.获取所有的身份信息,如果返回的是多条记录,那么resultType返回值类型,应该是集合的泛型-->
    <select id="getAllProvince" resultType="com.howie.domain.Province">
        select * from province;
    </select>
    <!--3.添加省份信息-->
    <!--
        注意:在未来实际项目开发中,所有的标签都必须要写id属性
        <select>标签,parameterType属性可以省略不写,resultType属性必须得写
        对于 <inert> <update> <delete>这3个标签,通常只写id属性,其他属性一概不写
    -->
    <insert id="insertProvince">
        insert into province(id,name,jiancheng,shenghui) values(#{id},#{name},#{jiancheng},#{shenghui});
    </insert>
    <!--4.根据id修改操作-->
    <update id="updateProvince">
        update province set name = #{name},jiancheng = #{jiancheng},shenghui = #{shenghui} where id = #{id};
    </update>
    <!--5.根据id删除操作-->
    <delete id="deleteProvince">
        delete from province where id = #{id};
    </delete>
</mapper>
ProvinceMapper.xml

src/com/howie/test/Test1.java

package com.howie.test;
import com.howie.domain.Province;
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 java.io.InputStream;
import java.util.List;

public class Test1 {
    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        // 输出流
        InputStream inputStream = null;
        try {
            // 通过加载MyBatis的主配置文件mybatis-config.xml,创建输入流对象
            inputStream = Resources.getResourceAsStream(resource);
        } catch(Exception e){
            e.printStackTrace();
        }
        /*
        SqlSessionFactoryBuilder:SqlSessionFactory的建造者。通过该建造者对象调用建造方法,为我们创建一个SqlSessionFactory对象

        sqlSessionFactory对象唯一的作用就是为我们创建SqlSession对象我们未来所有的操作,使用的都是SqlSession对象
         */
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession(); // Java与数据库之间的会话
        // 我们未来所有的操作(例如:增删改查,处理事务等),使用的都是SqlSession对象

        /*
        需求1:根据id查询单条数据
            如果取得的是单条记录,我们调用selectOne方法
            参数1:根据命名空间.sqlId的形式找到我们需要使用的SQL语句
            参数2:我们要为SQL语句中传递的参数

        Province province = session.selectOne("province.getById", 1);
        System.out.println(province);
        session.close();
        */

        // 需求2:查询省份信息表中所有省份信息
        /*
        List<Province> provinceList = session.selectList("province.getAllProvince");
        for(Province p:provinceList){
            System.out.println(p);
        }
        session.close();*/

        // 需求3:添加操作
        /*
        注意:MyBatis默认情况下是手动提交事务(session.commit(true));
         */
        /*
        Province province = new Province(10,"云南","滇","昆明");
        int updateColumnCount = session.insert("province.insertProvince", province);
        session.commit(false);
        if(updateColumnCount > 0){
            System.out.println("添加成功!");
        } else{
            System.out.println("添加失败!");
        }
        session.close();*/

        // 需求4:修改操作
        /*
        Province province = new Province(10,"云南","云","昆明");
        int updateColumnCount = session.update("province.updateProvince", province);
        session.commit(false);
        if(updateColumnCount > 0){
            System.out.println("修改成功!");
        } else{
            System.out.println("修改失败!");
        }
        session.close();*/

        // 需求5:根据id删除
        int updateCount = session.delete("province.deleteProvince",10);
        session.commit(false);
        if(updateCount > 0){
            System.out.println("删除成功!");
        } else{
            System.out.println("删除失败!");
        }
        session.close();
    }
}
Test1.java

5 - MyBatis解决JDBC存在的问题

1)获取连接、得到Statement、处理ResultSet、关闭资源非常繁琐,而在MyBatis中使用SqlSesstion搞定一切

2)将sql语句写死到java代码中,如果修改SQL语句,必须修改java代码,必须重新编译,程序可维护性不高,而在MyBatis中,将SQL语句配置在Mapper.xml文件中与java代码分离

3)向PreparedStatement 对占位符的位置设置参数时,非常繁琐。而Mybatis自动将java对象映射至SQL语句,通过statement中的 parameterType定义输入参数的类型

4)解析结果集时需要把字段的值设置到相应的实体类属性名中。而Mabatis自动将SQL执行结果映射至java对象,通过statement中的resultType定义输出结果的类型

4 - MyBatis结合Dao层的开发

1.MyBatis结合Dao层的操作

项目结构图

<1>dao层

dao/ProvinceDao.java

package com.howie.dao;

import com.howie.domain.Province;

public interface ProvinceDao {
    public Province getProvinceById(String id);
    public void insertProvince(Province province);
}
ProvinceDao.java (interface)

dao/ProvinceDaoImpl.java

package com.howie.dao;

import com.howie.domain.Province;
import com.howie.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class ProvinceDaoImpl implements ProvinceDao {
    @Override
    public Province getProvinceById(String id) {
        SqlSession session = SqlSessionUtil.getSession();
        return session.selectOne("province.getById",id);
    }

    @Override
    public void insertProvince(Province province) {
        SqlSession session = SqlSessionUtil.getSession();
        session.insert("province.save",province);
    }
}
ProvinceDaoImpl.java

<2>domain层(存放JavaBean略)

<3>mapper层

mapper/ProvinceMapper.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="province">
    <!--1.根据id,获取省份信息-->
    <select id="getById" parameterType="java.lang.String" resultType="com.howie.domain.Province">
        select * from province where id = #{id};
    </select>
    <!--2.添加省份信息-->
    <insert id="save">
        insert into province(id,name,jiancheng,shenghui) values(#{id},#{name},#{jiancheng},#{shenghui});
    </insert>
</mapper>
ProvinceMapper.xml

<4>service层

service/ProvinceService.java

package com.howie.service;

import com.howie.domain.Province;

public interface ProvinceService {
    public Province getProvinceById(String id);
    public void insertProvince(Province province);
}
ProvinceService.java (interface)

service/impl/ProvinceServiceImpl.java

package com.howie.service.impl;

import com.howie.dao.ProvinceDao;
import com.howie.dao.ProvinceDaoImpl;
import com.howie.domain.Province;
import com.howie.service.ProvinceService;

public class ProvinceServiceImpl implements ProvinceService {
    // 接口 = 接口实现类
    private ProvinceDao provinceDao = new ProvinceDaoImpl(); // 多态

    @Override
    public Province getProvinceById(String id) {
        return provinceDao.getProvinceById(id);
    }

    @Override
    public void insertProvince(Province province) {
        provinceDao.insertProvince(province);
    }
}
ProvinceServiceImpl.java

<5>test层

test/Test1.java

package com.howie.test;

import com.howie.domain.Province;
import com.howie.service.ProvinceService;
import com.howie.service.impl.ProvinceServiceImpl;
import com.howie.util.ServiceFactory;
import com.howie.util.TransactionInvocationHandler;

public class Test1 {
    public static void main(String[] args) {
        // 这里得到代理类(经济人)
        ProvinceService provinceService = (ProvinceService) ServiceFactory.getService(new ProvinceServiceImpl());

        // 测试业务1
        /*Province province = provinceService.getProvinceById("1");
        System.out.println(province);*/

        // 测试业务2
        Province province = new Province(10,"云南","滇","昆明");
        provinceService.insertProvince(province);
    }
}
Test1.java

<6>util层

util/ServiceFactory.java

package com.howie.util;

public class ServiceFactory {
    // 传递被代理对象(明星),得到代理对象(经济人)
    public static Object getService(Object service){
        return new TransactionInvocationHandler(service).getProxy();
    }
}
ServiceFactory.java

util/SqlSessionUtil.java

package com.howie.util;

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 java.io.IOException;
import java.io.InputStream;

public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;
    // 静态代码块的特点:随着类的加载而加载,并且只执行一次
    static {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    private SqlSessionUtil(){} // 单例模式

    // 设置共享变量,供每个线程使用(保证session唯一)
    private static ThreadLocal<SqlSession> t = new ThreadLocal<SqlSession>();
    // 获取SqlSession对象
    public static SqlSession getSession(){
        SqlSession session = t.get(); // get() 方法:获取与当前线程关联的ThreadLocal值
        if(session == null){
            session = sqlSessionFactory.openSession();
            t.set(session);
        }
        return session;
    }
    // 关闭SqlSession对象
    public static void closeSession(SqlSession session){
        if(session != null){
            session.close();
            t.remove(); // remove() 将与当前线程关联的ThreadLocal值删除
            /*
            此句代码必须得加,非常重要,因为服务器分配的线程执行完毕后并没有被销毁,而是回到线程池(Tomcat服务器自带)中
            必须强制释放共享变量,才能保证只有一个session,以免保证SQL语句执行不紊乱
             */
        }
    }
}
SqlSessionUtil.java

util/TransactionInvocationHandler.java

package com.howie.util;

import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TransactionInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象(明星)即被代理类

    public TransactionInvocationHandler(Object target) {
        this.target = target;
    }
    // 代理类的业务方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SqlSession session = null;
        Object obj = null;
        try{
            session = SqlSessionUtil.getSession();
            // 处理业务逻辑
            obj = method.invoke(target,args); // 被代理类的方法
            // 处理业务逻辑完毕后,提交事务
            session.commit();
        } catch(Exception e){
            if(session != null){
                session.rollback();
            }
            e.printStackTrace();
        } finally{
            SqlSessionUtil.closeSession(session);
        }
        return obj;
    }
    // 取得代理类对象(经济人),只能由此方法取得
    public Object getProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}
TransactionInvocationHandler.java

 

2.MaBatis对dao层动态代理的支持

1)我们以前用过动态代理,我们是在业务层(service)使用的,在业务层使用动态代理是为了实现事务管理,业务层的动态代理是我们自己手写的,业务层之所以要使用动态代理,是因为业务层本身就是用来处理业务逻辑的

2)事务相关的代码不方便放在业务层处理。所以我们想到使用代理类帮助业务层去处理。现在我们要在dao层也要加入动态代理dao 层之所以创建代理类,是因为写 dao层实现类本身就是一种不方便。

3)在结合了MyBatis的动态代理机制后,以后的实际项目开发,dao层的 impl就不写了。MyBatis的动态代理不用我们自己手写,在MyBatis中已经集成好的一种机制,我们直接拿来使用就可以了。

基于以上代码的改进(最终结构图)

 

为dao层加入动态代理

dao层

dao/ProvinceDao.java

package com.howie.dao;
import com.howie.domain.Province;
import java.util.List;
public interface ProvinceDao {
    /*
    注意这里的方法名:必须和标签的id属性相同
     */
    public Province getById(String id);
    public void save(Province province);

    List<Province> getAll();
}
ProvinceDao.java

dao/ProvinceDao.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.howie.dao.ProvinceDao">
    <!--1.根据id,获取省份信息-->
    <select id="getById" parameterType="java.lang.String" resultType="com.howie.domain.Province">
        select * from province where id = #{id};
    </select>
    <!--2.添加省份信息-->
    <insert id="save">
        insert into province(id,name,jiancheng,shenghui) values(#{id},#{name},#{jiancheng},#{shenghui});
    </insert>
    <!--3.查询所有省份信息-->
    <select id="getAll" resultType="com.howie.domain.Province">
        select * from province;
    </select>
</mapper>
ProvinceDao,xml

service层

service/impl/ProvinceServiceImpl.java

package com.howie.service.impl;

import com.howie.dao.ProvinceDao;
import com.howie.domain.Province;
import com.howie.service.ProvinceService;
import com.howie.util.SqlSessionUtil;

import java.util.List;

public class ProvinceServiceImpl implements ProvinceService {
    private ProvinceDao provinceDao = SqlSessionUtil.getSession().getMapper(ProvinceDao.class); // 帮我们创建实现类

    @Override
    public Province getById(String id) {
        return provinceDao.getById(id);
    }

    @Override
    public void save(Province province) {
        provinceDao.save(province);
    }

    @Override
    public List<Province> getAll() {
        return provinceDao.getAll();
    }
}
ProvinceServiceImpl.java

service/ProvinceService.java

package com.howie.service;
import com.howie.domain.Province;
import java.util.List;

public interface ProvinceService {
    public Province getById(String id);
    public void save(Province province);

    List<Province> getAll();
}
ProvinceService.java (interface)

MyBatis的动态代理:

List<City> selectCities(); // Dao层接口里的抽象方法

1.dao对象,类型是CityDao,权限名称是:com.howie.dao.CityDao。权限名称和namespace是一样的

2.方法名称,selectCities,这个方法就是mapper文件中的id值 selectCities

3.通过dao中方法的返回值也可以确定mybatis要调用的SqlSession的方法,

  如果返回值是list,调用的是SqlSession.selectList()方法

  如果返回值 int,或者是非list的,看mapper文件中 的标签insert update 就会调用SqlSession的insert,update等方法

mybatis的动态代理:mybatis根据dao的方法调用,获取执行sql语句的信息

mybatis根据你的接口,创建出一个dao接口的实现类,并创建这个类的对象。完成SqlSession调用方法,访问数据库。

 

使用mybatis的动态代理机制:

使用SqlSession.getMapper(dao接口.class),getMapper能获取dao接口对于的实现类对象

5 - MyBatis配置文件

1.properties 把连接与加载数据需要的信息封装到src/db.properties

src/db.properties

# 据库连接需要的4个基本信息
username=root
password=101323
url=jdbc:mysql://localhost:3306/springdb?useSSL=FALSE&serverTimezone=UTC&allowPublicKeyRetrieval=true
# jdbc:mysql://localhost:3306/springdb?useSSL=FALSE&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true
driver=com.mysql.cj.jdbc.Driver
db.properties

src/mybatis-config.xml   设置从db.properties中读取数据

<?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"/> <!--加载文件内容-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/> <!--事务管理-->
            <dataSource type="POOLED"> <!--数据源,连接池等-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <mapper resource="com/howie/dao/ProvinceDao.xml"/>
    </mappers>
</configuration>
mybatis-config.xml

2.settings  在主配置文件中(mybatis-config.xml)

<settings>
        <!--
            设置数据库交互的环境,例如可以在此处配置二级缓存,配置查询延迟加载策略等等
            配置的目的是为了更加有效的查询表中的记录
            在实际项目开发中,settings的设置基本没有用,因为settings对于查询的优化,得到的效果不明显

            对于海量级别的数据,使用settings配置优化,起不到任何的效果
            对于数据量较少的项目,对于查询的效率要求比较低,也没有必要使用settings配置
            如果遇到了海量级别的数据,我们如何去提高查询的效率?
            基础操作
                对于常用的查询条件的字段(如姓名),设置索引
            高级操作
                使用nosql数据库,redis
            专业操作
                针对于电商行业
                搜索引擎选择:Elasticsearch 与 solr
        -->
  <setting name="" value=""/>
</settings>

3.为mapper映射文件的domain起别名(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"/> <!--加载文件内容-->
    <!--为mapper映射文件的domain器别名-->
    <typeAliases>
        <!--
            方式1:为指定的类分别起别名,别名的命名由我们来决定
            <typeAlias type="com.howie.domain.Province" alias="pd"/>
            type:要为那个domain起别名,填写包.类名称
            alias:别名的名字
        -->
        <!--
            方式2:使用package标签批量起别名
            别名是MyBatis默认为我们取好的,命名不是由我们自己决定,别名为类(类名的字母不区分大小写)
            name:指定一个包结构,表示在该包下,所有的domain自动起好了别名
        -->
        <package name="com.howie.domain"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/> <!--事务管理-->
            <dataSource type="POOLED"> <!--数据源,连接池等-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <mapper resource="com/howie/dao/ProvinceDao.xml"/>
    </mappers>
</configuration>

总结:

  1)未来实际项目开发中,如果公司需要使用起别名的机制,我们要使用批量起别名的方式

  2)在市场上也有很多企业摒弃使用MyBatis起别名的机制,公司会认为将domain写成全路径,可以有效的提高代码的可读性

4.Mapper映射文件注册(mybatis-config.xml)

<mappers>
        <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <!--Mapper映射文件注册-->
        <!--方式1:使用resource属性-->
        <mapper resource="com/howie/dao/ProvinceDao.xml"/>
        <!--方式2:使用class属性,找到dao层接口的全路径-->
        <!--<mapper class="com.howie.dao.ProvinceDao"/>-->
        <!--方式3:批量注册。name属性:指向dao层的包,表示在该dao包下,所有的mapper映射文件自动注册,推荐使用-->
        <!--<package name="com.howie.dao"/>-->
</mappers>

6 - MyBatis映射文件

1.parameterType

设置参数类型:parameterType用于设置输出参数的Java类型,parameterType的值为参数类型的java类型或者别名,SQL语句获取参数的值使用#{}或者${},使用时可以省略

  1)使用基本数据类型为参数

  2)使用引用数据类型为参数

  3)使用map为参数

package com.howie.test;
import com.howie.dao.ProvinceDao;
import com.howie.domain.Province;
import com.howie.util.SqlSessionUtil;

import java.util.HashMap;
import java.util.Map;

public class Test2 {
    public static void main(String[] args) {
        // 接口 = 接口实现类
        ProvinceDao provinceDao = SqlSessionUtil.getSession().getMapper(ProvinceDao.class); // 创建实现类
        // 1.测试parameterType以基本数据类型或String为参数
        /*Province province = provinceDao.getById("10");
        System.out.println(province);*/
        /* 1.结论对于常用的数据类型系统已为我们起好了别名。例如:java.lang.String -- > String/string等,此时参数类型写别名即可
        <select id="getById" parameterType="String" resultType="province">
          2.写SQL语句时,使用8种基本数据类型+String为参数时,#{}中的标识符可以随意去写,如
          select * from student where id = #{id123}  但是虽然可以随意些,还是写的要见名知意
         */

        // 2.测试parameterType以引用数据类型为参数。需求:查询简称为滇,且id=10的省份信息(此时SQL语句有两个参数,但是调用查询语句时绝对不能同时传递两个参数)
        // 如果我们要为SQL语句传递多个参数,我们应该将这多个参数封装到一个domain对象中,或者是打包到一个map集合中
        /*Province province = new Province();
        province.setId(10);
        province.setJiancheng("滇");
        System.out.println(provinceDao.select1(province));*/
        /*
        注意点:如果我们为SQL语句传递的参数类型为一个domain引用类型,那么#{}中的标识符必须是domain类的属性名
        <select id="select1" parameterType="Province" resultType="Province">
            select * from province where id = #{id} and jiancheng = #{jiancheng};
        </select>
         */

        // 3.测试parameterType以map集合为参数。需求:查询名称为云南,且id=10的省份信息
        Map<String,Object> map = new HashMap<>();
        map.put("name","云南");
        map.put("id",10);
        Province province = provinceDao.select2(map);
        System.out.println(province);
        /*
        注意点:如果我们为SQL语句传递的参数类型为一个domain引用类型,那么#{}中的标识符必须是Map的key
         */

        /*
            总结:在实际项目开发过程中,使用domain引用类型,或者使用map集合类型都可以为SQL语句同时传递多个参数
            一般情况下,我们使用domain就可以了
            当domain不符合需求情况下,我们一定要考虑使用map来传值
         */
    }
}

2.#{}与${}

使用在SQL语句中的符号

1)#{}:表示占位符,可以有效防止SQL注入。使用#{}设置参数。无序考虑参数的类型 PreparedStatement

2)${}:表示拼接符,无法防止SQL注入,使用${}设置参数必须考虑参数的类型 Statement

3)传递简单类型参数

  a.如果获取简单类型参数,#{}中可以使用value或其他名称

  b.如果获取简单类型参数,${}中只能使用valeu。例如:select * from tbl_student where id='${value}'

4)在没有特殊要求的情况下,通常使用#{}占位符

5)有些情况必须使用${},比如需要动态拼接表名,select * from ${tablename} 比如:动态拼接排序字段:select * from tablename order by ${username} desc

6)重点案例:

  使用${}执行 like 模糊查询

  使用#{} 执行like 模糊查询

package com.howie.test;
import com.howie.dao.ProvinceDao;
import com.howie.domain.Province;
import com.howie.util.SqlSessionUtil;
import java.util.List;
public class Test2 {
    public static void main(String[] args) {
        // 接口 = 接口实现类
        ProvinceDao provinceDao = SqlSessionUtil.getSession().getMapper(ProvinceDao.class); // 创建实现类
        // 测试:like 模糊查询。方式1:${} --- 了解即可
        // 案例:查询省份名称带"江"的信息
        /*List<Province> provinceList = provinceDao.select3("江");
        for(Province p:provinceList){
            System.out.println(p);
        }*/

        // 测试:like 模糊查询。方式2:#{} 了解即可
        // 案例:查询省份名称带"江"的信息(注意这里传参时只能是以 "%参数%" 的形式传给SQL语句,但是这种传值方式显然是不合理的)
        /*List<Province> provinceList = provinceDao.select4("%江%");
        for(Province p:provinceList){
            System.out.println(p);
        }*/

        // 测试:like 模糊查询。方式3:#{}
        // 案例:查询省份名称带"江"的信息
        /*
        select * from province where name like '%' #{name} '%'; MySQL中空格相当于+号(拼接)
         */
        List<Province> provinceList = provinceDao.select5("江");
        for(Province p:provinceList){
            System.out.println(p);
        }
    }
}
View Code

 

3.resultType

设置返回值类型

  1)返回基本数据类型+String

  2)返回domain

  3)返回hashMap(注意返回值类型)

package com.howie.test;
import com.howie.dao.ProvinceDao;
import com.howie.domain.Province;
import com.howie.util.SqlSessionUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) {
        // 接口 = 接口实现类
        ProvinceDao provinceDao = SqlSessionUtil.getSession().getMapper(ProvinceDao.class); // 创建实现类
        // 测试:resultType  返回基本数据类型
        // 案例1:查询出id为10的省份名称
        /*String name = provinceDao.select6("10");
        System.out.println("省份名称:" + name);*/

        // 案例2:查询所有省份名称。返回 String
        /*List<String> nameList = provinceDao.select7();
        for(String name:nameList){
            System.out.println(name);
        }*/
        // 案例3:查询表中的省份数量,返回 int
        /*int count = provinceDao.select8();
        System.out.println(count);*/

        // 测试:resultType, 返回domain类型。略
        // 测试:resultType, 返回map类型。
        List<Map<String,Object>> mapList = provinceDao.select9();
        for(Map<String,Object> map:mapList){
            Set<String> keySet = map.keySet();
            for(String key:keySet){
                System.out.println("key:" + key);
                System.out.println("value:" + map.get(key));
            }
            System.out.println("-------------");
        }
        /*
        说明:对于SQL语句查询的结果,我们使用domain来封装这些结果很方便,为什么还要使用map呢?
        因为对于查询结果,很多情况,使用domain封装不了,所以我们会想到使用map来保存结果
        例如:
            根据省份名称分组,查询出来每一个姓名对应的数量
            select
            # 当使用group by 分组后select 后面只能使用聚合函数或者分组字段
            name,count(*)
            from province
            group by name;
            以上查询结果,不适合用domain进行封装(没有count(*)属性),适合使用map封装查询结果
         */
    }
}
View Code
查询结果封装过程解析:
<select id="" resultType="Student">
    select * from tbl_student;
</select>
当执行了SQL语句之后,通过查询得到的结果 id,name,age。根据返回值类型,会自动为我们创建出来一个该类型的对象,由该对象将查询的结果封装起来
Student s1 = new Student();
s1.setId(id);
s1.setName(name);
s1.setAge(age);
当查询出来了第二条记录,根据返回值类型,再一次创建出来一个对象,封装第二条记录的值
Student s2 = new Student();
s2.setId(id);
s2.setName(name);
s2.setAge(age);
...
...
多条记录封装成了多个Student对象,系统会自动的为我们创建出来一个List集合来保存这些对象
List<Student>list = ArrayList<>();
list.add(s1);
list.add(s2);
...
...
<select id="" resultType="hashMap">
    select * from tbl_student;
</select>
当执行了SQL语句之后,通过查询得到的结果 id,name,age。根据返回值类型没会自动为我们创建出来一个该类型的对象(map对象),由该对象将查询的结果保存起来
Map<String,Object> map1 = HashMap<String,Object>();
map1.put("id",id);
map1.put("name",name);
map1.put("age",age);

当查询出来第二条记录,根据返回值类型,再一次创建出来一个对象(map对象),保存第二条记录的值
Map<String,Object> map2 = HashMap<String,Object>();
map2.put("id",id);
map2.put("name",name);
map2.put("age",age);
...
...
多条记录封装为多个map对象
系统会自动的为我们创建出来一个List集合来保存这些map对象
List<Map<String,Object>> mapList = new ArrayList<>();
mapList.add(map1);
mapList.add(map2);
...
...
查询结果封装过程解析

  4)当查询字段名和domain属性名不一致时解决的方案

    a.为字段起别名,别名为类中属性名

      select id,fullname as name,age from tbl_student

    b.使用sesultMap

      id:resultMap标签对是唯一标识

      type:指定一个类型,与数据表一一对应,建立表字段和类属性的名字一一匹配的关系

      将来在使用到该resultMap标签的时候,使用id来找到这组标签

        <resultMap type="Student" id="stuMap">

          <id property="id" column="id"/> <!--id标签:用来配置主键的对应关系的-->

          <result property="name" column="fullname"/> <!--result标签:用来配置普通字段对应关系-->

          <result property="age" column="age"/>

          <!--

            property属性:配置的是类中的属性名

            column属性:配置的表中的字段名

            这样就能够建立起类属性和表字段一一对应的关系了

          -->

        </resultMap>

      <select id="selectStudent" resultMap="stuMap">

         select * from tbl_student

      </select>

7 - MyBatis动态SQL

1.用动态SQL

1)在实际开发过程中,往往会遇到各种各样的需求,我们不可能为每一个需求都创建SQL语句,肯定是要将SQL语句写成动态的形式。

2)动态SQL语句的核心思想就是,有那个查询条件,就动态的在 where 关键字后面挂在那个查询条件

2.代码示例

src/com/howie/test/Test2.java

package com.howie.test;
import com.howie.dao.ProvinceDao;
import com.howie.domain.Province;
import com.howie.util.SqlSessionUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) {
        // 接口 = 接口实现类
        ProvinceDao provinceDao = SqlSessionUtil.getSession().getMapper(ProvinceDao.class); // 创建实现类
        // 测试1:动态SQL where标签+if标签
        /*Province province = new Province();
        province.setShenghui("南");
        // province.setJiancheng("昆明");
        province.setName("江");
        List<Province> provinceList = provinceDao.select1(province);
        for(Province p:provinceList){
            System.out.println(p);
        }*/

        // 测试2:动态SQL foreach标签
        // 查询id为1,2,3,4,5,6的学员信息
        /*String[] arrayId = new String[]{"1","2","10"};
        List<Province> provinceList = provinceDao.select2(arrayId);
        for(Province p:provinceList){
            System.out.println(p);
        }*/

        // 测试3:SQL片段
        Province province = provinceDao.select3(10);
        System.out.println(province);
    }
}
View Code

src/com/howie/dao/ProvinceDao.xml

 <!--测试-->
    <select id="select1" parameterType="Province" resultType="Province">
        select * from province
        <!--where标签-->
        <!--
            当where标签在使用的时候,必须要搭配where标签对中的if标签来使用
            通过if标签的判断,如果有查询条件,则展现where关键字,如果没有查询条件则不展现where关键字
            where标签会自动的屏蔽掉第一个连接符 and/or
        -->
        <where>
            <if test="name!=null and name!=''">
                name like '%' #{name} '%';
            </if>
            <if test="jiancheng!=null and jiancheng!=''">
                and jianchegn like '%' #{jiancheng} '%';
            </if>
        </where>
    </select>

    <select id="select2" resultType="Province">
        select * from province where id in
        <!--
            foreach标签:用来遍历传递来的数组参数
            collection:标识传递参数的类型。array->数组  list->集合
            item:每一次遍历出来的元素,在使用该元素的时候,需要套用在#{}中
            open:拼接循环的开始符号
            close:拼接循环的结束符号
            separator:元素与元素之间的分隔符
        -->
        <!--例如:select * from province where id in(1,2,3,4); 可以写成如下代码-->
        <foreach collection="array" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

    <!--
    使用SQL标签制作SQL片段
    SQL片段的作用是用来代替SQL语句中的代码
    如果你的mapper映射文件中的SQL语句某些代码出现了大量的重复,我们可以使用SQL片段来代替他们
    id属性:SQL片段的唯一标识,将来找到SQL片段使用id来进行定位

    将来的实际项目开发中,使用SQL片段用来代替重复率高,且复杂的子查询
    -->
    <sql id="sql1">
        select * from province
    </sql>
    <select id="select3" parameterType="int" resultType="Province">
        <include refid="sql1"/> where id = #{id};
    </select>
View Code

8 - 多表查询案例

src/com/howie/test/Test2.java

package com.howie.test;
import com.howie.dao.ProvinceDao;
import com.howie.domain.Province;
import com.howie.util.SqlSessionUtil;
import com.howie.vo.ProvinceAndCityVo;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) {
        // 接口 = 接口实现类
        ProvinceDao provinceDao = SqlSessionUtil.getSession().getMapper(ProvinceDao.class); // 创建实现类
        // 测试多表查询
        // 案例1:查询省份名称和省份划分的城市
        /*List<Map<String,Object>> mapList = provinceDao.select1();
        for(Map<String,Object> map:mapList){
            Set<String> set = map.keySet();
            for(String key:set){
                System.out.println("省份:" + key);
                System.out.println("城市:" + map.get(key));
            }
        }*/

        // 案例2:查询所有省份和城市信息,加vo(view object)/(value object) 视图对象
        /*
        在实际项目开发中,如果需要为前端展现的数据,使用一个domain类型不足以表现出来这些数据
        这时我们可以考虑使用两种技术来实现,分别为
        使用map以及使用vo
            例如我们现在的需求(查询省份和城市所有信息)
            得到的结果,使用学生的domain或者班级的domain都不能够封装这些结果

            所以我们可以使用map去保存这些信息
            同时我们也可以使用vo类来保存这些信息,此时ov类需要我们自己创建出来,其属性由查询结果来决定
         */
        /*List<ProvinceAndCityVo> provinceAndCityList = provinceDao.select2();
        for(ProvinceAndCityVo pc:provinceAndCityList){
            System.out.println(pc);
        }*/

        // 案例3:查询省份名称带江字的省份信息和城市信息
        List<ProvinceAndCityVo> provinceAndCityVoList = provinceDao.select3("江");
        for(ProvinceAndCityVo vo:provinceAndCityVoList){
            System.out.println(vo);
        }
        /*
        总结:
        实际项目开发中,如果要为前端同时提供多组值,那么我们应该使用map还是vo呢?
        如果前端的需求的重复率不高,那么我们选择临时使用map就可以了
        如果前端对于需求的重复率较高,那么我们可以创建一个vo类来使用,非常方便
         */
    }
}
Test1.java

mapper映射文件

<select id="select1" resultType="Map">
         select p.name n,c.name from province p join city c where p.id = c.provinceid;
</select>

<select id="select2" resultType="com.howie.vo.ProvinceAndCityVo">
        select p.id pid,p.name pname,p.jiancheng,p.shenghui,c.id cid,c.name cname
        from province p join city c where p.id = c.provinceid;
</select>

<select id="select3" parameterType="String" resultType="ProvinceAndCityVo">
        select p.id pid,p.name pname,p.jiancheng,p.shenghui,c.id cid,c.name cname
        from province p join city c where p.id = c.provinceid and p.name like '%' #{name} '%';
</select>
View Code

 

posted @ 2020-08-12 09:22  赖正华  阅读(170)  评论(0编辑  收藏  举报