MyBatis

一:简介:

  作用: 数据访问层框架.

    底层是对 JDBC 的封装.

  mybatis :优点之一:
    使用 mybatis 时不需要编写实现类,只需要写需要执行的 sql 命

二:环境搭建(这里创建的是web项目)

  注意:这里并没有写对应的显示类,这就是Mybatis的有点之一,他通过反射的方式,将对应的mapper.xml文件动态的生成实现类,来执行了;

    1)创建maven项目,目录结构

    

    2)pom.xml

<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>

    3)mybatis-conf.xml

    (这个文件跟spring无关,因为这时并没有一如spring但却要创建mybatis全局配置文件,我这里配了一个模板(也可以到mybatis文档中复制一份),mybatis-conf-xml,注意修改mapper.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-->
    <properties resource="db.properties"/>
    <!--取别名-->
    <typeAliases>
        <!--这种方式只能一个一个类的起别名,太麻烦-->
        <!--<typeAlias type="com.xpl.model.User" alias="User"/>-->
        <!--这种方式可以指定给某个包下的所有类起别名,并且可以写多个package-->
        <package name="com.xpl.model"/>
    </typeAliases>
    <!--default随意起名字,value是下边environment的id意思就是当前使用的数据源是什么-->
    <environments default="development">
        <!--environment可以配置多个数据源-->
        <environment id="development">
            <!-- 使用原生JDBC事务 -->
            <transactionManager type="JDBC"/>
            <!--type中pooled是数据连接池-->
            <!--<dataSource type="POOLED">-->
                <!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
                <!--<property name="url" value="jdbc:mysql:///test"/>-->
                <!--<property name="username" value="root"/>-->
                <!--<property name="password" value="root"/>-->
            <!--</dataSource>-->
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--配置mapper.xml文件到mybatis中-->
    <mappers>
        <!--resource是mapper.xml的全文件名(区别于全路径名,全路径名中间是".",这里是"/"),适用于,多个mapper.xml文件一个一个的精准的写入-->
        <mapper resource="com/xpl/mapper/UserMapper.xml"/>
        <!--这种方式用来和接口式编程的方式配合,扫描mapper接口所在德包-->
        <!--<package name="com.xpl.mapper"/>-->
    </mappers>
</configuration>

 

     4)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">
<!--namespace是命名空间,相当于是取一个唯一的名字,用于区分,建议使用对应的类的全路径名-->
<mapper namespace="com.xpl.pojo.User">
    <!--result是返回值类型,因为mybatis是一行一行的读,所以读一行就会封装一个对应的对象,所以返回值类型的对应的类-->
    <select id="selectUser" resultType="com.xpl.pojo.User">
    select * from USER ;
  </select>
</mapper>

    5)Test.java

public class Test {
    public static void main(String[] args) throws IOException {
//        通过流加载配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
//        通过构建模式来创建SqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//        打开session
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        执行sql
        List<User> users = sqlSession.selectList("com.xpl.model.User.selAllUser");
        System.out.println(users);
//        记得提交和关闭
        sqlSession.commit();
        sqlSession.close();
    }
}

    6)日志文件(加上这个文件并在pom中引入日志依赖:作用,运行的时候我们可以在控制台上看到生成的sql)

我这里的log4j文件是制作的模板,一键生成的
具体的日志的详细,后期会写
log4j.rootLogger=DEBUG,stdout log4j.logger.org.mybatis=DEBUG log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n

    7)db.properties

db.driver=com.mysql.jdbc.Driver
db.username=root
db.password=root
db.url=jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8

 三:分析,理解:

    这里主要的文件有四个:

    一:pom.xml

      因为我们这里虽然创建的是web项目,但是我们使用main方法启动的,并没有使用web容器启动;

      所以只需要加mybatis和数据库驱动的依赖

      因为我们把配置文件写在了resource中Idea是默认在src中加载(classpath:路径下)

      所以我们要配置buil属性将默认路径进行更改

    二:mapper.xml文件

      这个文件的作用就是给程序员写sql的

      因为mybatis的优点之一就是不需要实现类,会根据mapper.xml文件通过反射机制动态创建实现,

      所以我们只写mapper就可以了

      因为namespace定义了命名空间确定唯一性,所以名字可以随意,只要不重复

      但是建议使用对应类的全路径名

    三:mybatis.xml文件

      配置的属性有:

        数据源:

          事务类型

          数据源配置

        mapper的引入

    四:Test.java
      使用构建模式通过SqlSessionFactory加载mybatis.xml文件并创建SqlSession,并开启session

      session通过mapper中的命名空间+id来操作数据库

四:全局配置文件属性解析:

    1<transactionManager/> type 属性可取值

 <environments default="development">
        <!--environment可以配置多个数据源-->
        <environment id="development">
            <!-- 使用原生JDBC事务 -->
            <transactionManager type="JDBC"/>
            <!--type中pooled是数据连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>

      1.1 JDBC,事务管理使用 JDBC 原生事务管理方式

      1.2 MANAGED 把事务管理转交给其他容器.原生 JDBC 事务

          底层实现:setAutoMapping(false); 就是关闭自动提交,事务交给谁谁来处理;
    1.2 <dataSouce/>type 属性

       <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>

      1.2.1 POOLED 使用数据库连接池

      1.2.2 UNPOOLED 不实用数据库连接池,和直接使用 JDBC 一样

      1.2.3 JNDI :java命名和目录技术调用其他技术时使用

五:三种查询方式:

    selectList(返回值是List集合

<mapper namespace="com.xpl.model.User">
  <select id="selectUser" resultType="com.xpl.pojo.User">
      select * from USER ;
  </select>
</mapper>


List<User> users = sqlSession.selectList("com.xpl.pojo.User.selectUser");

输出:

User{userName='xxx', address='xxx', id=1}
User{userName='xxx', address='xxx', id=2}
User{userName='xxx', address='xxx', id=3}
User{userName='xxx', address='xxx', id=4}

 

    selectOne(返回值是一个变量或者对象的时候)

<mapper namespace="com.xpl.model.User">
    <select id="selectOne" resultType="String">
         select  userName from USER where id=1;
  </select>
</mapper>


<mapper namespace="com.xpl.model.User">
    <select id="selOne" resultType="com.xpl.model.User">
        select * from user where id=1; ;
    </select>
</mapper>

 

    selectMap(返回值是一个Map

      (Map<Integer, User> id = sqlSession.selectMap("com.xpl.pojo.User.selectMap", "id");

      中的“id”将会封装成结果集返回的map集合中的key:将数据库的那个列的值作为key

      

mapper.xml

<
select id="selectMap" resultType="com.xpl.pojo.User"> select * from user </select>

main.java
Map<Integer, User> id = sqlSession.selectMap("com.xpl.pojo.User.selectMap", "id"); 输出: {1=User{userName='xxx', address='xxx', id=1}, 2=User{userName='xxx', address='xxx', id=2}, 3=User{userName='xxx', address='xxx', id=3}, 4=User{userName='xxx', address='xxx', id=4}}

 六:参数的传递(对五的补充)

  传递单个基本数据类型

      <!--parameterType是指传入的参数类型-->

    //通过parameteType设置传入值的类型,可以取到传入的基本类型的值 <select id="selOne" resultType="com.xpl.pojo.User" parameterType="int"> select * from USER where id=#{0}; </select>
         // 通过传递一个基本数据类型的数据进行查询 User user1 = sqlSession.selectOne("com.xpl.pojo.User.selOne", 1); System.out.println(user1);     输出结果: User{userName='xxx', address='xxx', id=1}

  传递多个基本数据类型

    <!--多个参数需要传入一个封装了多个参数的map--> 
      //注意!!!:传入的map集合名字可以随便起,但是paramete中的类型必须是map或者是map的全路径名(因为paramete表示的是传入的参数类型)
      //通过parametaType设置传入的是一个map:map中封装了传进来的多个参数,取值的时候一定要注意#{id}中的key是传入的map中的key!
<select id="selMap" resultType="com.xpl.pojo.User" parameterType="map"> select * from user where id=#{id} and userName=#{userName}; </select>
     
    //通过传递一个map进行多参数的查询
    //将要传的值封装到map中,key是字段名 value是要传的值 
        Map<String,Object> page=new HashMap<String,Object>();
        page.put("id",2);
        page.put("userName","xxx");
        User user2 = sqlSession.selectOne("com.xpl.pojo.User.selMap", page);
        System.out.println(user2);
    输出结果:
User{userName
='xxx', address='xxx', id=2}

  传递对象

    通过paremater设置传入的类型时一个对象,这个对象中的属性值就是我们要去取得值,这个对象中可能之封装了个别参数,不需要管,只用去取我们需要的参数 
    <select id="selObj" resultType="com.xpl.pojo.User" parameterType="com.xpl.pojo.User"> select * from USER where id=#{id} </select>
    //创建一个对象,然后将对象传递过去
    //通过传递一个对象进行传值
        User user3 =new User();
        user3.setId(3);
        User user4 = sqlSession.selectOne("com.xpl.pojo.User.selObj", user3);
        System.out.println(user4);
    输出结果:
User{userName
='xxx', address='xxx', id=3}

  传递包装类型(这样插入的值还是user,而不是userW):

<mapper namespace="com.xpl.model.UserW">
   <insert id="addUser" parameterType="UserW">
       insert into user set userName=#{user.userName},address=#{user.address},id=#{user.id},tid=#{user.tid};
   </insert>
</mapper>
public class UserW {
    private User user;
public class User {
    private int id;
    private String userName;
    private String address;
    private int tid;

七:$和#的区别:

    #{}:

      使用parameteStatement进行占位符操作,通过日志发现sql中是使用?进行占位,然后将传入的参数进行填充

    ${}:

      使用Statement的方式,进行字符串拼接,不从传入的参数中将其填充,而是使用get/set方法获取的值,

      ${内容}中的“内容”直接进行字符串拼接,

      ${数字}如果内容是数字,直接填数字

    我们常规使用#{}的方式进行,但是当遇到有模糊匹配like的时候还是要去使用${}来进行

八:setting设置

    1.在 mybatis 全局配置文件中通过<settings>标签控制 mybatis 全局开关

九:分页查询:

Map<String,Integer> map=new HashMap<String, Integer>();
//        查询第几页
        int pageNumber=1;
//        一页显示多少条数据
        int pageSize=3;
//        从第几个开始查询,也就是将从第几页开始查转换为数据库中limit中的第一个参数

//        Mapper.xml文件中取参数时,#{}不支持运算,所以要在这里将其算出来,
//        limit中的两个参数表示:1.从哪一行开始,2.查询几条。
        int pageStart=(pageNumber-1)*pageSize;
        map.put("pageSize",pageSize);
        map.put("pageStart",pageStart);
        List<User> users = sqlSession.selectList("com.xpl.pojo.User.selPage", map);
        for(User user1:users){
            System.out.println(user1);
        }
<select id="selPage" resultType="com.xpl.pojo.User" parameterType="map">
        select * from user limit #{pageStart},#{pageSize};
</select>

十:别名

方式一:
<!--给某个类起个别名--> <typeAliases> <typeAlias type="com.xpl.pojo.User" alias="user"/> </typeAliases>
方式二:
<typeAliases>
<!--给某个包下所有的实体类都起个别名-->
<package name="com.xpl.pojo"/>
</typeAliases>
注意:使用package的方式是默认别名是类名(首字母要大写)

  这个时候我们就不用写全类名了

起别名之前的写法:
<select id="selObj" resultType="com.xpl.pojo.User" parameterType="com.xpl.pojo.User">
    select * from USER where id=#{id}
</select>
起别名之后的写法:
<select id="selObj" resultType="user" parameterType="user">
    select * from USER where id=#{id}
</select>

 十一:增,删,改(没有也不用写resultType属性)

  增:

<insert id="addBook1" parameterType="com.itbaizhan.bean.Book">
    insert into book (name,author) values (#{name},#{author})
</insert>
public void test2(){
    Book book=new Book();
    book.setAuthor("曹雪芹");
    book.setName("红楼梦");
    int insert = sqlSession.insert("book.addBook1", book);
    System.out.println(insert);
}

  删:

<delete id="deleteUser" parameterType="java.lang.String">
    delete from user where username=#{username};
</delete>
public void test4(){
   Book book=new Book();
   book.setName("三国演义");
   book.setId(1l);
    int delete = sqlSession.delete("com.xpl.User.deleteUser",user);
    System.out.println(delete );
}

  改:

<update id="upBookById" parameterType="com.itbaizhan.bean.Book">
    update book set name=#{name} where id=#{id}
</update>
public void test4(){
   Book book=new Book();
   book.setName("水浒传");
   book.setId(1l);
    int upBookById = sqlSession.update("upBookById", book);
    System.out.println(upBookById);
}

十二:事务

  首先明白三个概念:

    功能:从程序角度触发,需要完成的,比如转账就是一个功能:用户A给用户B转钱

    业务:从代码角度出发,增曲实现功能的过程对应一个service的方法:比如转账的业务先在A账户减去500块钱,再在B账户增加500块钱;

    事务:是指完成一个功能的方法中所执行的sql的集合;比如转账过程先执行A账户减去500的sql语句,在执行B账户增加500块钱的sql语句,这两条sql就是一个事务管理;

          mybatis是对jdbc的封装,默认是不会自定提交事务的(关闭了自动提交功能)

    在openSession()时Mybatis会创建sqlSession,同时创建一个Transaction(事务对象),

    每一个 SqlSession 默认都是不自动提交事务.那么就需要通过session.commit()提交事务.或者通过设置为自动提交事务openSession(true);底层相当于是setAutoCommit(true);

public class Test {
    public static void main(String[] args) throws IOException {
//        通过流加载配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
//        通过构建模式来创建SqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//        打开session
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
    }
}

  回滚:    

    因为可能执行了多条sql但是某一条会出现异常,那么提交之后就会出现数据错乱的情况

    解决的办法就是将所有的代码都用try-cath包裹起来,然后在catch中执行rollback这样程序可以正常运行,并且数据库数据是正常的

  事例:(这里只是做一个示例演示一下事务和回滚,理论上在service中是不建议try-catch的,后边有方法处理)

     User user = new User();
        user.setId(12);
        user.setAddress("周口");
        user.setUserName("高蒙");
        try {
            int res = sqlSession.update("com.xpl.pojo.User.insertUser", user);
            System.out.println(res);

        }catch (Exception e){
            sqlSession.rollback();
        }
//        mybatis默认关闭了自动提交所以这里要提交一下事务
        sqlSession.commit();

十三:接口绑定方案及多参数传递(以后常用的方式

  分析:

    1.创建一个和mapper.xml文件名称一样的接口,底层通过动态代理实现了这个接口,并且接口中的方法和mapper.xml中的id一致

    2.mybatis全局文件中更改配置mapper.xml的方式,使用package的方式定义mapper存在的位置

    3.调用的时候,先通过sqlSession加载mapper接口,获取session,通过session来执行接口中的方法;

<?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>
    <!--给某个类起个别名-->
    <typeAliases>
        <!--<typeAlias type="com.xpl.pojo.User" alias="user"/>-->
        <!--给某个包下所有的实体类都起个别名-->
        <package name="com.xpl.pojo"/>
    </typeAliases>
    <!--default随意起名字,value是下边environment的id意思就是当前使用的数据源是什么-->
    <environments default="development">
        <!--environment可以配置多个数据源-->
        <environment id="development">
            <!-- 使用原生JDBC事务 -->
            <transactionManager type="JDBC"/>
            <!--type中pooled是数据连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--配置mapper.xml文件到mybatis中-->
    <mappers>
        <!--resource是mapper.xml的全文件名(区别于全路径名,全路径名中间是".",这里是"/")-->
        <!--<mapper resource="com/xpl/mapper/UserMapper.xml"/>-->
     <!--这种方式只能用在接口式编程,因为他扫的是mapper.xml--> <package name="com.xpl.mapper"/> </mappers> </configuration>
public interface UserMapper {
    List<User> selAllUser(int id,String userName);
}
<?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">
<!--这里的namesqace必须和mapper接口的全路径名一致 id必须和接口中的方法一致-->
<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="selAllUser" resultType="User">
        select * from user where id=#{0} and userName=#{1} ;
    </select>
</mapper>
public class Test {
    public static void main(String[] args) throws IOException {
        InputStream is =Resources.getResourceAsStream("mybatis-conf.xml");
//        使用构建者设计模式
//        原因是,SqlSessionFactory的构造方法中使用了configuration,太麻烦直接一步到位
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//        开启session
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.selAllUser(1, "徐沛蕾");
        for (User u:users) {
            System.out.println(u);
        }
    }
}

  传值问题:(解决了传值不方便问题)

  因为在mapper.xml中只能通过下表(0.1.2...或者param1.param2....来获取比较麻烦解决办法为)

  在接口中定义传入的参数

public interface UserMapper {
    List<User> selAllUser(@Param("id") int id,@Param("userName") String userName);
}

  那么在mapper中就可以直接通过参数名来获取了

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="selAllUser" resultType="User">
        select * from user where id=#{id} and userName=#{userName} ;
    </select>
</mapper>

  注意:

    1.接口中@param中的参数名跟后边的id和userName没有关系,他和mapper.xml文件中取值时的{}里边的名字是一致的(有点起个别名的意思)

    2.mapper.xml标签中不用写参数类型parameterType

十四:动态sql

  if:

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="selAllUser" resultType="User">
        select * from user where 1=1
        <if test="id !=null and id !=''">
            and id=#{id}
        </if>
        <if test="userName !=null and userName != ''">
            and userName=#{userName} ;
        </if>
    </select>
</mapper>

  where:

    功能:

      1.去掉and

      2.自动生成或者不给where

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="selAllUser" resultType="User">
        select * from user
        <where>
            <if test="id !=null and id !=''">
                and id=#{id}
            </if>
            <if test="userName !=null and userName != ''">
                and userName=#{userName} ;
            </if>
        </where>
    </select>
</mapper>
执行语句:

  List<User> users = mapper.selAllUser(null,"赵晓鹏");

生成的sql:

  //第一个参数为空被判断掉了,sql中的if标签中的and也没有了,只有后边的,并且有where
  select * from user WHERE userName=? 
  //如果两个参数都为bull
  select * from user

  choose  when:只要有一个成立其他不执行

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="selAllUser" resultType="User">
        select * from user
        <where>
            <choose>
                <when test="id !=null and id !=''">
                    and id=#{id}
                </when>
                <when test="userName !=null and userName != ''">
                    and userName=#{userName} ;
                </when>
            </choose>
        </where>
    </select>
</mapper>
List<User> users = mapper.selAllUser(null,"徐沛蕾");

生成的sql:
  select * from user WHERE userName=?


//两个参数都满足但是只执行了id
List<User> users = mapper.selAllUser(1,"徐沛蕾");
生成的sql:
  select * from user WHERE id=?

  set:

      1.生成set

      2.去掉最后一个,

<?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">
<!--这里的namesqace必须和mapper接口的全路径名一致-->
<mapper namespace="com.xpl.mapper.UserMapper">
    <update id="upDateUser" parameterType="User">
        update user
        <set>
       id=#{id};//这一句的作用就是防止set语句中所有的判断语句不成立导致sql语句出错变成uptate user where id=?这是一个错误的sql语句
            <if test="userName != null and userName != ''">
                userName=#{userName},
            </if>
            <if test="address != null and address != ''">
                address=#{address},//这个逗号不加也不会报错
            </if>
        </set>
        where
                id=#{id}
    </update>
</mapper>
int res = mapper.upDateUser(2, "张学良","东北");
生成的sql:
     update user SET userName=?, address=? where id=? 

  trim:

    可以选择在前边加上什么去掉什么和后边加上什么去掉什么

<mapper namespace="com.xpl.mapper.UserMapper">
    <update id="upDateUser" parameterType="User">
        update user
       <trim prefix="set" suffixOverrides=",">
           id=#{id},
           <if test="userName !=null and userName !=''">
               userName=#{userName},
           </if>
           <if test="address !=null and address !=''">
               address=#{address},
           </if>
       </trim>
        where id=#{id}
    </update>
</mapper>
int res = mapper.upDateUser(1, "刘德华","香港");

生成sql:
  update user set id=?, userName=?, address=? where id=? 

  补充知识:

    模糊匹配输入值和数据库值需要添加前缀或者后缀的解决方案

      方式一:代码中编写

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="mhupipei" parameterType="string" resultType="User">
        select * from user where userName like #{userName};
    </select>
</mapper>
String userName="%"+"1"+"%";
List<User> users = mapper.mhupipei(userName);

      方式二:通过动态sql:bind

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="mhupipei" parameterType="string" resultType="User">
        <bind name="userName" value="'%'+userName+'%'"/>
        select * from user where userName like #{userName};
    </select>
</mapper>
String userName="1";
List<User> users = mapper.mhupipei(userName);

      方式三:使用${}

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="mhupipei" parameterType="string" resultType="User">
        select * from user where userName like '%${userName}%'
    </select>
</mapper>
String userName="1";
List<User> users = mapper.mhupipei(userName);

      方式四:(使用sql关键字concat)

select * from book where name like concat ('%',#{name},'%')

  bind:(重新赋值)

    一:模糊查询

    二:需要在传入的值的前后加内容

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="mhupipei" parameterType="string" resultType="User">
        <bind name="userName" value="'%'+userName+'%'"/>
        select * from user where userName like #{userName};
    </select>
</mapper>
String userName="1";
List<User> users = mapper.mhupipei(userName);

  知识补充:多条件模糊查询

User user =new User();
user.setId(2);
user.setUserName("");
user.setAddress("2");
List<User> users = mapper.mhupipei(user);
for (User u:users) {

System.out.println(u);
}
sqlSession.commit();
方式一:

注意绑定的位置!!!
<mapper namespace="com.xpl.mapper.UserMapper"> <select id="mohupipei" parameterType="User" resultType="User"> select * from user <where> <if test="id !=null and id !=''"> <bind name="id" value="'%'+id+'%'"/> and id like #{id} </if> <if test="userName !=null and userName !=''"> <bind name="userName" value="'%'+userName+'%'"/> and userName like #{userName} </if> <if test="address !=null and address !=''"> <bind name="address" value="'%'+address+'%'"/>       and address like #{address} </if> </where> </select> </mapper>
方式二:

<
mapper namespace="com.xpl.mapper.UserMapper"> <select id="mohupipei" resultType="User" parameterType="User"> select * from user <where> <if test="id !=null and id !=''"> and id like '%${id}%' </if> <if test="userName !=null and userName !=''"> and userName like '%${userName}%' </if> <if test="address !=null and address !=''"> and address like '%${address}%' </if> </where> </select> </mapper>

  foreach:  

    功能:

      循环参数内容,还具备在内容的前后添加内容,还具备添加分隔符功能. 
    适用场景:

      in 查询中.批量新增中(mybatis 中 foreach 效率比较低)

List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(4);
        List<User> users = mapper.getUserByForeach(list);
        for (User u:users) {
            System.out.println(u);
        }
<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="getUserByForeach" resultType="User" parameterType="list">
        select * from USER where id in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>
</mapper>

  sql片段:

    相当于是一个sql字段复用

List<User> users = mapper.sqlRefId();
for (User u:users) {
    System.out.println(u);
}
<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="sqlRefId" resultType="User" parameterType="list">
        select <include refid="temp"></include>
        from user
    </select>

    <sql id="temp">
        id,userName,address
    </sql>
</mapper>

十五:缓存

    1. 应用程序和数据库交互的过程是一个相对比较耗时的过程

    2. 缓存存在的意义:让应用程序减少对数据库的访问,提升程序运行效率 

    3. MyBatis 中默认 SqlSession 缓存开启

      3.1 同一个 SqlSession 对象调用同一个<select>时,只有第一次访问数据库,第一次之后把查询结果缓存到 SqlSession 缓存区(内存)中

      3.2 缓存的是 statement 对象.(简单记忆必须是用一个<select>)

      3.2.1 在 myabtis 时一个<select>对应一个 statement 对象

      3.3 有效范围必须是同一个 SqlSession 对象

    4. 缓存流程

      4.1 步骤一: 先去缓存区中找是否存在 statement

      4.2 步骤二:返回结果

      4.3 步骤三:如果没有缓存 statement 对象,去数据库获取数据

      4.4 步骤四:数据库返回查询结果

      4.5 步骤五:把查询结果放到对应的缓存区中

    5. SqlSessionFactory 缓存
      5.1 又叫:二级缓存
      5.2 有效范围:同一个 factory 内哪个 SqlSession 都可以获取
      5.3 什么时候使用二级缓存:
        5.3.1 当数据频繁被使用,很少被修改
      5.4 使用二级缓存步骤
        5.4.1 在 mapper.xml 中添加
        5.4.2 如果不写 readOnly=”true”需要把实体类序列化

      5.5 当 SqlSession 对象 close()时或 commit()时会把 SqlSession 缓存的数据刷(flush)到 SqlSessionFactory 缓存区中

十六:多表联查

    有三种方式:

      1.业务装配

        就是在service层中查出一个,之后根据查出来的数据进行查询(N+1)

      2.AutoMapping,

        通过别名完成映射

      3.通过resultMap来完成

    属性含有有对象:

      1.单个对象

      2.集合对象

     ResultMap原理:默认使用空参构造(常用方式)也可以使用有参构造(但是实体类中必须有对应的有参构造)以后的例子都是用无参,这里只做有参的说明

  <resultMap id="myResultMap" type="User">
        <constructor>
            <idArg column="id" name="arg0" javaType="java.lang.Integer" jdbcType="INTEGER"/>
            <arg column="userName" name="arg1" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <arg column="address" name="arg2" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <arg column="tid" name="arg3" javaType="int" jdbcType="INTEGER"/>
        </constructor>
    </resultMap>

    要想参数不写arg0...这种形式,就必须在传值的时候使用@parmar(“name”)

<resultMap id="myResultMap" type="User">
        <constructor>
            <idArg column="id" name="id" javaType="java.lang.Integer" jdbcType="INTEGER"/>
            <arg column="userName" name="userName" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <arg column="address" name="address" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <arg column="tid" name="tid" javaType="int" jdbcType="INTEGER"/>
        </constructor>
    </resultMap>

 

    一)单表resultMap:解决数据库字段名称和实体类名不一致问题

      数据库表设计:

      

  

public class User {
    private String userName1;
    private String address1;
    private int id1;
<mapper namespace="com.xpl.mapper.UserMapper">
    <resultMap id="myResultMap" type="User">
        <id column="id" property="id1"/>
        <result column="userName" property="userName1"/>
        <result column="address" property="address1"/>
    </resultMap>
    <select id="selAll" resultMap="myResultMap">
        select * from user;
    </select>
</mapper>
List<User> users = mapper.selAll();

    二:多表resultMap:(关联一个对象

public class Teacher {
    private int id;
    private String name;


public class User {
    private String userName;
    private String address;
    private int id;
    private int tid;
    private Teacher teacher; 
<mapper namespace="com.xpl.mapper.UserMapper">
              //查的是什么这里类型是什么
    <resultMap id="myResultMap" type="User">
    下边前两行可以不配置,但是第三行一定要配上,因为mybatis只会讲关联的字段装配一次  
        1.<id column="id" property="id"/>
        2.<result column="userName" property="userName"/>
        3.<result column="tid" property="tid"/>
        <association> 装配一个对象时使用
        property: 对象在类中的属性名
        select:通过哪个查询查询出这个对象的信息
        column: 把当前表的哪个列的值做为参数传递给另一个查询

        <association property="teacher" column="tid" select="com.xpl.mapper.TeacherMapper.selTeacherBytid"/>
    </resultMap>
    <select id="selAll" resultMap="myResultMap" parameterType="int">
        select * from user where id=#{id};
    </select>
</mapper>
<mapper namespace="com.xpl.mapper.TeacherMapper">
    <select id="selTeacherBytid" resultType="Teacher" parameterType="int">
        select * from teacher where id=#{id};
    </select>
</mapper>
public interface TeacherMapper {
    Teacher selTeacherBytid(@Param("id") int id);
}
public interface UserMapper {
    List<User> selAll(@Param("id")int id);
}
 List<User> users = mapper.selAll(1);

 

二:多表resultMap:(关联一个list

public class User {
    private String userName;
    private String address;
    private int id;
    private int tid;
    private Teacher teacher;

public class Teacher {
    private int id;
    private String name;
    private List<User> users;
<mapper namespace="com.xpl.mapper.UserMapper">
                          这里没有resultMap所以是resultType
    <select id="selAll" parameterType="int" resultType="user">
        select * from user where tid=#{tid};
    </select>
</mapper>
<mapper namespace="com.xpl.mapper.TeacherMapper">
    <resultMap id="myresultMap" type="teacher">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
      users对应老师中的list属性名   id:想要将teacher中的那一列的值拿来进行user查询   select对应user的查询
        <collection property="users" column="id" ofType="user" select="com.xpl.mapper.UserMapper.selAll"/>
    </resultMap>
    <select id="selTeacher" resultMap="myresultMap" >
        select * from teacher;
    </select>
</mapper>
多表联查加载的是先查询的那个mapper.xml
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

List<Teacher> teachers = mapper.selTeacher();

  注意:这里可以将关联的那个resultMap抽取出来继承

<mapper namespace="com.xpl.mapper.TeacherMapper">
    <resultMap id="myresultMap" type="teacher">
        <id column="id" property="id"/>
    </resultMap>

    <resultMap id="myresultMap2" type="teacher" extend="myresultMap”>
      users对应老师中的list属性名   id:想要将teacher中的那一列的值拿来进行user查询   select对应user的查询
        <collection property="users" column="id" ofType="user" select="com.xpl.mapper.UserMapper.selAll"/>
    </resultMap>
    <select id="selTeacher" resultMap="myresultMap" >
        select * from teacher;
    </select>
</mapper>

 

  三:多表联合查询(常用方式

    和上边的二对比:

        查询谁就写谁的mapper.xml和接口不需要写两个

<mapper namespace="com.xpl.mapper.TeacherMapper">

        <!--注意因为我们的sql中起了别名,所以这里我们的column中要和sql】中的别名一致-->
        <!--property中的和类名一致-->
    <resultMap id="myresultMap" type="teacher">
        <id column="tid" property="id"/>
        <result column="tname" property="name"/>
        <collection property="users" ofType="user">
            <id column="uid" property="id"/>
            <result column="uname" property="userName"/>
            <result column="uaddress" property="address"/>
            <result column="utid" property="tid"/>
        </collection>
    </resultMap>
    <select id="selTeacher" resultMap="myresultMap" >
        select t.id tid,t.name tname,u.id uid,u.userName uname,u.address uaddress,u.tid utid from teacher t left join user u on t.id=u.tid;
    </select>
</mapper>

    四:AutoMapping加载单个对象这种方式只能resultType而不能resultMap

      起个别名:然后遇到对象属性就通过`teacher.name` 来赋值

<mapper namespace="com.xpl.mapper.UserMapper">
    <select id="selAll" resultType="User">
        select u.id id,u.userName userName,u.address address,u.tid tid,t.name `teacher.name`,t.id `teacher.id` from user u left join teacher t on t.id=u.tid;
    </select>
</mapper>

  五:延迟加载

    (懒加载,实体类中有其他属性,在sql中又引用了其他的sql,name我们在text中查询的时候,不去是用那个相关的属性,

        就只会执行主的那一条sql,只用使用了相关联的属性,懒加载才会去加载关联的sql)

    配置可以在mapper.xml中fetchType="lazy"也可以在全局配置文件中配置

 

<mapper namespace="com.xpl.mapper.UserMapper">
              //查的是什么这里类型是什么
    <resultMap id="myResultMap" type="User">
    下边前两行可以不配置,但是第三行一定要配上,因为mybatis只会讲关联的字段装配一次  
        1.<id column="id" property="id"/>
        2.<result column="userName" property="userName"/>
        3.<result column="tid" property="tid"/>
        <association> 装配一个对象时使用
        property: 对象在类中的属性名
        select:通过哪个查询查询出这个对象的信息
        column: 把当前表的哪个列的值做为参数传递给另一个查询
      <association property="teacher" column="tid" select="com.xpl.mapper.TeacherMapper.selTeacherBytid" fetchType="lazy"/>
</resultMap> <select id="selAll" resultMap="myResultMap" parameterType="int"> select * from user where id=#{id}; </select> </mapper>

十七:配置类型转换器

  看图理解:

  

    实现:

    一)实体类

public class People {
    private String name;
    private String address;
    private List<String> likes;

    二)配置转换器类

package com.xpl.handler;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;

import java.sql.*;
import java.util.Arrays;
import java.util.List;
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class ListHandler implements TypeHandler<List<String>> {

    //        这里的操作就是原生jdbc中的动态的设置值
    public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
        StringBuffer sb=new StringBuffer();
//        parameter是我传进来的参数集合,我需要将他遍历出来,一个一个的a按照下标给她赋值
        for(String s:parameter){
            sb.append(s).append(",");
        }
//        给PreparedStatement设置值 需要两个参数:下标,列名
        ps.setString(i,sb.toString());
    }
//    下边三个是重载方法,用来读取的时候使用
    public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
//        将查询结果中的值按照列名读出来
        String string = rs.getString(columnName);
//        将上边的字符串变成数组,按照,划分
        String[] strings = string.split(",");
//        将数组变成集合,看类中定义的是什么,我的类中定义的是list就变成list,如果定义的是数组,直接返回就可以了
        List<String> values = Arrays.asList(strings);
        return values;

    }

    public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
        //        将查询结果中的值按照列名读出来
        String string = rs.getString(columnIndex);
//        将上边的字符串变成数组,按照,划分
        String[] strings = string.split(",");
//        将数组变成集合,看类中定义的是什么,我的类中定义的是list就变成list,如果定义的是数组,直接返回就可以了
        List<String> values = Arrays.asList(strings);
        return values;
    }

    /**
     * 这个是用来调用存储过程的时候使用
     * @param cs
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
        //        将查询结果中的值按照列名读出来
        String string = cs.getString(columnIndex);
//        将上边的字符串变成数组,按照,划分
        String[] strings = string.split(",");
//        将数组变成集合,看类中定义的是什么,我的类中定义的是list就变成list,如果定义的是数组,直接返回就可以了
        List<String> values = Arrays.asList(strings);
        return values;
    }
}

    三)mapper

<?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.xpl.model.People">
   <insert id="addPeople" parameterType="People">
       insert into people set name=#{name},address=#{address},likes=#{likes,typeHandler=com.xpl.handler.ListHandler}
   </insert>
    <select id="selPeople" parameterType="string" resultType="People">
        select * from people where name=#{name};
    </select>
</mapper>

    四)test

      添加时:这里使用的是mybatis全局配置文件配置的转换器

public class Test {
    public static void main(String[] args) throws IOException {
//        通过流加载配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
//        通过构建模式来创建SqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//        打开session
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        执行sql
        List<String> list = new ArrayList<String>();
        list.add("足球");
        list.add("排球");
        list.add("篮球");
        People people = new People();
        people.setName("xu");
        people.setAddress("宝安");
        people.setLikes(list);
        int res = sqlSession.insert("com.xpl.model.People.addPeople", people);
        System.out.println(res);
        
//        记得提交和关闭
        sqlSession.commit();
        sqlSession.close();
    }
}

      读取时:这里的转换器在mapper.xml文件中添加

package com.xpl.test;


import com.xpl.model.People;
import com.xpl.model.User;
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;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) throws IOException {
//        通过流加载配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
//        通过构建模式来创建SqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//        打开session
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        执行sql
        People res = sqlSession.selectOne("com.xpl.model.People.selPeople", "xu");
        System.out.println(res);
//        记得提交和关闭
        sqlSession.commit();
        sqlSession.close();
    }
}

  Mybatis-conf.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-->
    <properties resource="db.properties"/>
    <!--取别名-->
    <typeAliases>
        <!--这种方式只能一个一个类的起别名,太麻烦-->
        <!--<typeAlias type="com.xpl.model.User" alias="User"/>-->
        <!--这种方式可以指定给某个包下的所有类起别名,并且可以写多个package-->
        <package name="com.xpl.model"/>
    </typeAliases>
    <!--配置类型转换器:注意他的位置!!!!!,跳坑了!!!-->
    <typeHandlers>
        <!--这个是在读取的时候需要用到-->
        <!--包扫描,将所有的类型转换器都扫进来-->
        <!--<package name="com.xpl.handler"/>-->
        <!--一个一个的配置类型转换器-->
        <typeHandler handler="com.xpl.handler.ListHandler"/>
    </typeHandlers>
    <!--default随意起名字,value是下边environment的id意思就是当前使用的数据源是什么-->
    <environments default="development">
        <!--environment可以配置多个数据源-->
        <environment id="development">
            <!-- 使用原生JDBC事务 -->
            <transactionManager type="JDBC"/>
            <!--type中pooled是数据连接池-->
            <!--<dataSource type="POOLED">-->
                <!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
                <!--<property name="url" value="jdbc:mysql:///test"/>-->
                <!--<property name="username" value="root"/>-->
                <!--<property name="password" value="root"/>-->
            <!--</dataSource>-->
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--配置mapper.xml文件到mybatis中-->
    <mappers>
        <!--resource是mapper.xml的全文件名(区别于全路径名,全路径名中间是".",这里是"/"),适用于,多个mapper.xml文件一个一个的精准的写入-->
        <mapper resource="com/xpl/mapper/UserMapper.xml"/>
        <!--这种方式用来和接口式编程的方式配合,扫描mapper接口所在德包-->
        <!--<package name="com.xpl.mapper"/>-->
    </mappers>
</configuration>

十八:主键回填:

    因为可能在某些情况下id或者某些字段是自动生成的,比如取餐码等等,回填到刚才插入的那个对象中,

  有两种:

    1)数据库的字段自增长

    2)数据库字段是我们在代码中自动生成的uuid

  方式一(自增长)

 

<mapper namespace="com.xpl.model.User">
   <insert id="addUser" parameterType="User">
--          回填的列名  插入前返回还是插入后返回     java类型           数据库列名         java列名
        <selectKey order="AFTER" resultType="java.lang.Integer" keyColumn="id" keyProperty="id">
            select last_insert_id()
        </selectKey>
       insert into user set userName=#{userName},address=#{address},id=#{id},tid=#{tid};
   </insert>
</mapper>

 

 

 

 

public class Test {
    public static void main(String[] args) throws IOException {
//        通过流加载配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
//        通过构建模式来创建SqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//        打开session
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        执行sql
        User user = new User();
        user.setUserName("krrrr");
        user.setAddress("zhoukou");
        user.setTid(1);
//        UserW userW = new UserW();
//        userW.setUser(user);
        int res = sqlSession.insert("com.xpl.model.User.addUser", user);
        System.out.println(res);
        System.out.println(user);
        sqlSession.commit();
        sqlSession.close();
    }
}
发现上边的输出语句,打印插入的那个没有设置id值的user,已经将插入时自定生成的id值回填到user中了;

 

  简化写法:

<mapper namespace="com.xpl.model.User">
   <insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id" keyColumn="id" >
       insert into user set userName=#{userName},address=#{address},id=#{id},tid=#{tid};
   </insert>
</mapper>

 

    方式二:uuid

<mapper namespace="com.xpl.model.User">
   <insert id="addUser" parameterType="User">
--          回填的列名  插入前返回还是插入后返回     java类型           数据库列名         java列名
        <selectKey order="BEFORE" resultType="java.lang.String" keyColumn="userName" keyProperty="userName">
            select uuid()
        </selectKey>
       insert into user set userName=#{userName},address=#{address},id=#{id},tid=#{tid};
   </insert>
</mapper>

 

public class Test {
    public static void main(String[] args) throws IOException {
//        通过流加载配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
//        通过构建模式来创建SqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//        打开session
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        执行sql
        User user = new User();
        user.setAddress("zhoukou");
        user.setTid(1);
        user.setId(10);
//        UserW userW = new UserW();
//        userW.setUser(user);
        int res = sqlSession.insert("com.xpl.model.User.addUser", user);
        System.out.println(res);
        System.out.println(user);//        记得提交和关闭
        sqlSession.commit();
        sqlSession.close();
    }
}

 

十九:注解

  简化xml的配置

    代码和sql耦合度太高,不方便,所以这里就先不写了

二十:Mybatis运行原理

 

   MyBatis运行开始时需要先通过 Resources 加载全局配置文件.下面需要实例化SqlSessionFactoryBuilder构建器.帮助SqlSessionFactory接口实现类DefaultSqlSessionFactory.        

  实例化DefaultSqlSessionFactory之前需先创建 XmlConfigBuilder解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把Configuratin传递给DefaultSqlSessionFactory.到此SqlSessionFactory工厂创建成功.

   SqlSessionFactory 工厂创建 SqlSession. 每次创建 SqlSession 时,都需要由 TransactionFactory 创建 Transaction对象,同时还需要创建 SqlSession 的执行器 Excutor,最后实例化DefaultSqlSession,传递给 SqlSession 接口. 根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作. 如果事务执行失败,需要进行 rollback 回滚事务. 如果事务执行成功提交给数据库.关闭 SqlSession









 

posted @ 2018-10-09 22:00  凤凰山小旋风  阅读(276)  评论(0编辑  收藏  举报