框架学习—MyBatis

框架学习—MyBatis(自我学习用)

一、概述
链接: link.

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

二、第一个MyBatis程序

步骤:
1、搭建环境
①、打开IDEA,创建一个maven项目。
②、打开设置,看maven是不是IDEA自带的,是的话改成自己电脑本地的版本
③、删除当前工程目录下的src(我们要把当前工程作为父工程,好处是可以对公共部分的包统一管理、导入;具体工程可以新建一个module)
④、导包:需要用的包有mysql驱动、mybatis、Junit、lombok等

2、创建工程
①、新建一个module,作为具体工程
②、编写核心配置文件mybatis-config.xml
③、编写mybatis工具类(把通过SqlSessionFactory获取SqlSession封装成一个工具类)
④、编写代码:实体类、Dao/mapper接口、接口实现类(这里就是一个Mapper配置文件,由原来的编写UserDaoImpl转换为编写一个UserMapper.xml文件)
⑤、进行测试

具体代码如下:

核心配置文件mybatis-config.xml,放在src/main/resources目录下

<?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核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

	//每一个mapper文件都要在核心配置文件中注册
    <mappers>
        <mapper resource="com/wl/dao/UserMapper.xml"/>
    </mappers>
    
</configuration>

mybatis工具类,可以方便的获取SqlSession对象
通过SqlSessionFactoryBuilder获取SqlSessionFactory ,再通过SqlSessionFactory 获取SqlSession

public class MyBatisUtils {

    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);

    }

    //提供通过sqlSessionFactory获得SqlSession的方法
    public static SqlSession getSqlSession(){

        return sqlSessionFactory.openSession();

    }
}

实体类pojo

这里直接使用lombok生成有参构造,无参构造,getter、setter、toString等方法

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}

Dao/Mapper接口

public interface UserDao {
    List<User> getUserList();
}

接口实现类/Mapper.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绑定一个对应的Dao/Mapper接口 -->
<mapper namespace="com.wl.dao.UserDao">

    <!--注释:id绑定对应接口的方法名     resultType设置对应方法的返回值类型 -->
    <select id="getUserList" resultType="com.wl.pojo.User">
        select * from mybatis.user;
    </select>

</mapper>

<!--注释:   注意,Mapper写完了要到核心配置文件中注册才能生效     -->

测试

public class UserDaoTest {

    @Test
    public void test(){

        //1、获取 SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //这里相当于通过sqlSession.getMapper方法获取一个UserDao的实现类对象
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        //通过mapper对象就可以执行类中定义的方法
        List<User> userList = mapper.getUserList();

        for (User user : userList) {
            System.out.println(user);
        }

        //最要关闭sqlSession
        sqlSession.close();
    }
}

需要注意的问题
①、由于maven约定大于配置,因此有可能会导出不了Mapper.xml文件,解决办法是在父工程的pom.xml文件中加入以下代码即可。

<!--由于maven约定大于配置,所以要在build中配置resources,来防止资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

②、每个Mapper.xml文件都要在核心配置文件mybatis-config.xml中注册才能生效

三、CRUD

现在我们的主要操作都是在mapper/dao(例如UserMapper)接口和mapper.xml(例如UserMapper.xml)文件中进行,也就是说除了这两部分,其他部分的代码都不用进行修改。

首先在mapper/dao接口中写CRUD方法,然后在mapper.xml进行相应的配置。代码如下

public interface UserMapper {

    //查询全部用户
    List<User> getUserList();

    //根据id查询用户
    User getUserById(int id);

    //添加用户
    int addUser(User user);

    //修改用户
    int updateUser(User user);

    //根据id删除用户
    int deleteUser(int id);

}
<?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绑定一个对应的Dao/Mapper接口 -->
<mapper namespace="com.wl.dao.UserDao">

    <!--id绑定对应接口的方法名     resultType设置对应方法的返回值类型 -->
    <select id="getUserList" resultType="com.wl.pojo.User">
        select * from mybatis.user;
    </select>

	<!--查-->
    <select id="getUserById" parameterType="int" resultType="com.wl.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>

	<!--增-->
    <insert id="addUser" parameterType="com.wl.pojo.User">
        insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd})
    </insert>

	<!--改-->
    <update id="updateUser" parameterType="com.wl.pojo.User">
        update mybatis.user set name = #{name} ,pwd=#{pwd} where id=#{id}
    </update>

	<!--删-->
    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id = #{id}
    </delete>

</mapper>

参数说明:
namespace:绑定一个对应的Dao/Mapper接口
id:接口中的函数名
parameterType:方法传入参数类型
resultType:方法返回值类型

测试代码:

由于操作大都一样,这里只放了增的操作,需要注意的是增删改操作要提交事务才能成功(sqlSession.commit()@Test
    public void addUser(){

        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserDao mapper = sqlSession.getMapper(UserDao.class);

        mapper.addUser(new User(4,"牵牛花","123456"));

        //注意增删改操作要提交事务才能成功
        sqlSession.commit();

        sqlSession.close();
    }

注意事项:增删改操作要提交事务才能成功,即要执行sqlSession.commit()才行

使用map来传参
当实体类或者数据库中的表,字段或者参数过多,可以考虑使用map来传参。因为map传参更加灵活,使用map我们可以只传递我们需要修改的值,而不用像传入实体类(比如User)一样,需要传递所有字段

代码如下:

	1、写接口函数
	//通过map添加一个用户
    int addUser2(Map<String,Object> map);
	2、mapper里添加配置
	<!-- 这里values 里的参数名就要和map中保持一致-->
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user (id, name, pwd) values (#{userid},#{username},#{userpwd})
    </insert>
	3、进行测试:
	@Test
    public void addUser2(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("userid",4);
        map.put("username","油菜花");
        map.put("userpwd","000101");

        mapper.addUser2(map);

        sqlSession.commit();
        sqlSession.close();

    }

四、配置解析

1、核心配置文件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核心配置文件-->
<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="com/wl/dao/UserMapper.xml"/>
    </mappers>
</configuration>

参数解释:
environments(环境配置) :MyBatis 可以配置成适应多种环境。尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境

transactionManager(事务管理器):默认设置为JDBC

dataSource(数据源):默认设置为POOLED

properties(属性):可以通过properties来引入外部配置文件(例如数据库配置文件db.properties)

db.properties也要放在src/main/resources目录下,内容如下

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
username=root
password=root

然后在核心配置文件中引入该配置文件

 <!--引入外部配置文件-->
 	可以直接引入
    <properties resource="db.properties" />
    
    也可以在引入文件的同时,在properties 内部增加一些属性配置
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>

	但需要注意的是,如果内部和外部属性配置有相同字段,则优先使用外部的

类型别名(typeAliases):类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

可以使用typeAlias给某个类起别名

	<typeAliases>
        <typeAlias type="com.wl.pojo.User" alias="User"/>
    </typeAliases>

也可以使用package指定一个包名,MyBatis会在包名下面搜索需要的Java Bean(实体类),默认使用类名首字母小写作为别名

	<typeAliases>
        <package name="com.wl.pojo"/>
    </typeAliases>

还可以通过注解给类起别名,并且通过注解设置的别名将优先使用

@Alias("user")
public class User {
    private int id;
    private String name;
    private String pwd;
}

设置别名后,在mapper文件里的参数类型就可以不使用类的全限定名,直接使用类的别名即可

	<select id="getUserList" resultType="user">
        select * from mybatis.user;
    </select>

mappers(映射器):每个mapper配置文件都需要到核心配置文件中进行注册,才能生效

方式一:通过resource

	<mappers>
        <mapper resource="com/wl/dao/UserMapper.xml"/>
    </mappers>

方式二:通过class

	<mappers>
        <mapper class="com.wl.dao.UserMapper"/>
    </mappers>

方式三:通过package

	<mappers>
        <package name="com.wl.dao"/>
    </mappers>

需要注意的是:通过class和package 注册的话,需要保证接口和它的mapper配置文件必须同名且在同一个包下

五、ResultMap结果集映射

ResultMap主要作用就是解决实体类属性名与数据库字段名不一致的问题
解决实体类属性名与数据库字段名不一致有两种办法,一是起别名,二就是使用ResultMap

1、起别名

	比如实体类的属性名password和数据库字段名pwd不一致
	
	<select id="getUserById" parameterType="int" resultSet="user">
        select id,name,pwd password from mybatis.user where id = #{id}
    </select>

2、通过ResultMap结果集映射

<!--结果集映射:    column表示字段名 property表示实体类属性名-->
	<!--id代表该结果集映射   type表示实际指向的类型-->
    <resultMap id="UserMap" type="user">
        <!--<result column="id" property="id"/>-->
        <!--<result column="name" property="name"/>-->
        <result column="pwd" property="password"/>
    </resultMap>

    <select id="getUserById" parameterType="int" resultMap="UserMap">
        select * from mybatis.user where id = #{id}
    </select>

六、日志

日志的好处是,不止会输出结果,还会输出其他的一些数据库相关信息

如图:
在这里插入图片描述
1、在mybatis核心配置文件中配置日志

<!--在核心配置文件中配置日志-->
    <settings>
        <!--标准的日志工厂实现 STDOUT_LOGGING-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

七、通过map传参,实现Limit分页查询

	1、接口:
	//通过map传参,实现分页查询
    List<User> getUserByLimit(Map<String,Integer> map);
	2、mapper.xml配置
	<select id="getUserByLimit" resultMap="UserMap">
        select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>
	3、测试:
	@Test
    public void getUserByLimit(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("startIndex",0);
        map.put("pageSize",2);

        List<User> userList = mapper.getUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }

        sqlSession.close();
    }

八、使用注解开发

除了mapper.xml配置文件完成语句的映射,还可以使用 Java 注解来配置完成语句的映射。使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句(比如实体类属性名和数据库字段名不匹配的情况),Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。

代码如下:

1、接口:
public interface UserMapper {
	//这里通过注解完成语句的映射
    @Select("select * from mybatis.user")
    List<User> getUsers();

}
2、在核心配置文件中绑定接口
	<mappers>
        <mapper class="com.wl.dao.UserMapper"/>
    </mappers>
3、测试
	@Test
    public void getUsers(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();

        for (User user : users) {
            System.out.println(user);
        }

        sqlSession.close();

    }

本质上是通过反射机制实现,底层是动态代理。

注解实现CRUD:

//当有函数有多个参数时,则每个参数前要加上@Param(),并且sql语句中的字段名名要与@Param()里的参数名保持一致。
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);
    
    // 引用类型的参数前则不用加@Param()
    @Insert("insert into user (id,name,pwd) values(#{id},#{name},#{password})")
    int addUser(User user);
    
    @Update("update user set name=#{name},pwd=#{password} where id=#{id}")
    int updateUser(User user);
    
    @Delete("delete from user where id=#{id}")
    int deleeUserById(@Param("id") int id);

关于@Param():
①、接口方法中基本类型参数或者String类型的参数,需要加上
②、引用类型参数则不用加
③、当方法中仅有一个为基本类型的参数的话,可以忽略不加
④、sql语句中引用的就是@Param()中设定的属性名

九、association和collection

1、association:多对一

两种方式:子查询、联表查询

学生实体类接口
@Data
public class Student {

    private int id;
    private String name;
    //多个学生关联一个老师
    private Teacher teacher;
}

老师实体类接口
@Data
public class Teacher {

    private int id;
    private String name;
}

 	<!--
        多对一(association)查询的两种方式:子查询、联表查询
    -->

    <!--方式一:子查询-->
    <select id="getStudents" resultMap="StudentMap">
        select * from student
    </select>

    <resultMap id="StudentMap" type="Student">
        <result property="id" column="id" />
        <result property="name" column="name"/>
        <!--javaType表示属性类型,select表示要使用的子查询-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id=#{tid}
    </select>

    <!--方式二:联表查询-->
    <select id="getStudents2" resultMap="StudentMap2">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid=t.id
    </select>

    <resultMap id="StudentMap2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" column="tid" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

2 、collection:一对多

@Data
public class Teacher {

    private int id;
    private String name;

    //一个老师对应多个学生
    List<Student> students;
}

@Data
public class Student {

    private int id;
    private String name;
    private int tid;
}
	<!--
        一对多查询(collection)的两种方式:
        按照结果嵌套处理
        按照查询嵌套处理
    -->

    <!--方式一:按照结果嵌套处理-->
    <select id="getTeacherById" resultMap="TeacherMap">
        select s.id sid, s.name sname, t.id tid, t.name tname
        from student s, teacher t
        where s.tid = t.id and tid = #{tid}
    </select>

    <resultMap id="TeacherMap" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--ofType表示集合中的泛型类型-->
        <collection property="students" ofType="Student">
            <result property="id" column="tid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>


    <!--方式二:按照查询嵌套处理-->
    <select id="getTeacherById2" resultMap="TeacherMap2">
        select * from teacher where id=#{tid}
    </select>

    <resultMap id="TeacherMap2" type="Teacher">
        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudent" column="id"/>
    </resultMap>

    <select id="getStudent" resultType="Student">
        select * from mybatis.student where tid = #{id}
    </select>

javaType:用来指定实体类中属性的类型
ofType: 用来指定集合中泛型的类型

十、动态SQL

动态 SQL 是 MyBatis 的强大特性之一,

常用的标签有:
if、choose (when, otherwise)、trim (where, set)、foreach

代码测试如下:

if标签

	1、接口
	//使用if查询
	List<Blog> queryBlogIF(Map map);
	2、mapper配置
	<select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <if test="author != null">
               author = #{author}
            </if>
            <if test="title != null">
                and title = #{title}
            </if>
        </where>
    </select>
	3、测试
	@Test
    public void queryBlogIF(){

        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        //map.put("title","蜡笔小新2");
        map.put("author","狂神说");

        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }

choose标签:

	//通过choose查询,类似于java中的switch case
	List<Blog> queryBlogChoose(Map map);
	<select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

set、where标签:

	//更新用户信息
	int updateBlog(Map map);
	<update id="updateBlog" parameterType="map">
        update mybatis.blog
        <set>
            <if test="title != null"> title = #{title}, </if>
            <if test="author != null">author = #{author} </if>
        </set>
        where id = #{id}
    </update>

总结:动态SQL就是在拼接SQL语句

十一、Mybatis缓存
链接: link.

缓存的目的就是提升查询的效率和减少数据库的压力,MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。MyBatis 默认开启一级缓存。

1、一级缓存(本地缓存):

一级缓存也叫本地缓存,MyBatis 的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis 的一级缓存是默认开启的,不需要任何的配置。

在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。

为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。

一级缓存的生命周期
①、MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
②、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
③、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;
④、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;

2、二级缓存

二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库。

开启二级缓存的方法:
①、在核心配置文件添加设置

	<setting name="cacheEnabled" value="true"/>

②、在mapper.xml文件中配置开启二级缓存标签

	可以不进行配置
	<cache/>

	也可以进行一些自定义配置
	<cache
  		eviction="FIFO"
  		flushInterval="60000"
  		size="512"
  		readOnly="true"/>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值