Mybatis

1. Mybatis

在这里插入图片描述

2. 通过SqlSessionFactory获取SqlSession

通过SqlSessionFactory获得Sqlsession,SqlSession中包含了面向数据库执行SQL命令所需的所有方法

  • 获取SqlSession的工具类
//SqlSessionFactory 生产 sqlsession
//工具类,全部都是静态的
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        String resource = "mybatis-config.xml";
        try {
            //通过流读取资源
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static SqlSession getSession(){
        //通过sqlSessionFactory.openSession() 获得sqlsession,参数为true时就不需要手动commit
        SqlSession sqlSession=sqlSessionFactory.openSession(true);
        return sqlSession;
    }
}
  • mybatis-config.xml,包含了一些连接池、连接数据库的信息
  • 注意! 需要在这里面注册Mapper关联的接口
<?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"/>
	<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=false&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
		<!--============================================================-->
		<!--用properties配置数据库信息,environment id 可以配置多个连接方式-->
		<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>
	<!-- 注意! 需要在这里面注册Mapper关联的接口-->
	<mappers>
		<mapper resource="com/ning/dao/UserMapper.xml"></mapper>
	</mappers>
</configuration>


3. 创建Mapper

  1. 原本用jdbc的方式写是这样的:
    创建接口、创建接口的实现类、在实现类的方法里写sql语句

  2. mybatis的方式:
    创建接口、抛弃了实现类,换用xml配置(mapper)的方式

  • 创建一个mapper(在这之前我写好了一个叫UserMapper的接口,和一个User的实体类)
    • 在jdbc中,查询完语句会有返回值,在mapper中用resultType或者resultMap来设定方法返回值类型
<?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 = 绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.ning.dao.UserMapper">

	<!--绑定了接口,就相当于实现了接口,也需要实现接口中的方法-->
	<!--id = 绑定接口中的方法 , resultType = 方法的返回值类型-->
	<select id="getUserAll" resultType="com.ning.pojo.User">
		select * from user
	</select>
</mapper>
  • 运行测试(在每次用完sqlsession的时候都需要关闭它,可以把这个关闭操作放到finally块中)
public class UserDaoTest {
     @Test
    public void test(){
        SqlSession sqlSession =MybatisUtils.getSession();
        try{
            // 获取SqlSession,直接调用写好的MybatisUtils工具类
            // 通过动态代理和反射机制返回了一个Mapper的实现类(这时候Mapper.xml就转换成实现类了)
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //直接调用接口中的方法,会对应mapper中绑定好 (select的id)执行里面的sql语句并返回结果
            List<User> list =  userMapper.getUserAll();
            for (User user : list) {
                System.out.println(user.getId());
            }
        }catch (Exception e){
            
        }finally {
            sqlSession.close();
        }
    }
}

3.1 ResultMap(映射结果集)

如果sql语句中查询的字段起了别名,那么在column中也需要写入同样的别名

<mapper namespace="com.ning.dao.UserMapper">
	<resultMap id="UserMap" type="User"> 
		<!-- id为主键 --> 
		<id column="id" property="id"/> 
		<!-- column是数据库表的列名 , property是对应实体类的属性名 --> 
		<result column="name" property="name"/> 
		<result column="pwd" property="password"/> 
	</resultMap>
	<!--返回值类型为上面起好别名封装好的resultMap id-->
	<select id="selectUserById" resultMap="UserMap"> 
		select id , name , pwd from user where id = #{id} 
	</select>
</mapper>


4. CRUD

  • select
    • id :对应的namespace中的方法名;
    • resultType: Sql语句执行完的返回值;
    • parameterType: 传入的参数类型;
	<select id="getUserById" resultType="com.ning.pojo.User" parameterType="int">
		select * from user where id = #{id}
	</select>
  • insert (增删改需要提交事务,在执行sql语句之后需要sqlSession.commit() )
	<insert id="addUser" parameterType="com.ning.pojo.User" >
		insert into mybatis.user(id,name,password) values (#{id},#{name},#{password})
	</insert>
  • update
	<update id="updateUser" parameterType="com.ning.pojo.User" >
		update user set name = #{name},password=#{password} where id=#{id}
	</update>
  • delete
		<delete id="deleteUser" parameterType="int">
		delete from user where id=#{id}
	</delete>
  • 用map集合传递参数(参数过多且项目体量小适合使用)
    • parameterType=“map”
    • sql语句中的参数为map的key
		<delete id="deleteUserByMap" parameterType="map">
		delete from user where id= #{helloid}
		</delete>
    @Test
    public void deleteUserByMap(){
        Map<String,Object> map =new HashMap<String, Object>();
        map.put("helloid",9);
        int num = userMapper.deleteUserByMap(map);
        sqlSession.commit();
        if (num>0){
            System.out.println("修改成功");
        }
        sqlSession.close();
    }
  • Limit分页
	<select id="getUserByLimit" resultMap="UserMap" parameterType="map">
		select * from `user` limit #{startIndex},#{pageSize}
	</select>
   public void getUserByLimit(){
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("startIndex",0);
        map.put("pageSize",2);
        List<User> list = userMapper.getUserByLimit(map);
        for (User user : list) {
            System.out.println(user);
        }
    }

5. 配置解析

5.1 核心配置文件

  • mybatis-config.xml
  • mybatis的配置文件包含了mybatis的行为设置和属性信息
configuration(配置)
   properties(属性) 
   settings(设置) 
   typeAliases(类型别名)
   typeHandlers(类型处理器) 
   objectFactory(对象工厂)
   plugins(插件)
   environments(环境配置) 
   		environment(环境变量)
   			transactionManager(事务管理器) 
   			dataSource(数据源) 
   databaseIdProvider(数据库厂商标识) 
   mappers(映射器)

5.2 属性 (properties)

  • properties配置文件形式注册数据库信息
<properties resource="db.properties"/>

5.3 类型别名(typeAliases)

  • typeAliases给实体类设置别名(不区分大小写),mapper中的返回值类型可以直接写别名
  • @Alias("user")或者可以在实体类上用注解形式起别名(需要在typeAliases下的package中加入需要扫描的包)
  • package在没有注解的情况下会使用Bean的首字母小写的非限定类名来作为他的别名
	<!--第一种-->
	<typeAliases>
		<typeAlias type="com.ning.pojo.User" alias="user"/>
	</typeAliases>
	
	<!--第二或第三种-->
	<typeAliases>
		<package name="com.ning.pojo"/>
	</typeAliases>

5.4 映射器(mappers)

作用:定义映射SQL语句文件

  • 使用相对于类路径的资源引用
<mappers> 
	<mapper resource="org/mybatis/builder/PostMapper.xml"/> 
</mappers>
  • 使用完全限定资源定位符(URL)
<mappers> 
	<mapper url="file:///var/mappers/AuthorMapper.xml"/> 
</mappers>
  • 使用映射器接口实现类的完全限定类名(需要配置文件名称和接口名称一致,并且位于同一目录下)
<mappers> 
	<mapper class="org.mybatis.builder.AuthorMapper"/> 
</mappers>
  • 将包内的映射器接口实现全部注册为映射器(需要配置文件名称和接口名称一致,并且位于同一目录下)
<mappers> 
	<package name="org.mybatis.builder"/> 
</mappers>

5.5 设置日志(log4j)

  1. 在mybatis-config.xml中设置日志工具
<settings>
	<setting name="logImpl" value="LOG4J"/> 
</settings>
  1. 导入log4j的包
	<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
	</dependency>
  1. log4j.properties
### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

5.6 设置下划线驼峰自动转换

	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>

6. Mybatis注解

缺点明显,只能用于简单的sql语句

@Select()用在接口中的需要查询的方法上

    @Select("select * from user")
    List<User> getUserAll();

@Param给参数命名,基本类型的参数或者string类型需要加上,引用类型不需要加

	@Select("select * from user where id=#{id}")
    List<User> getUserById(@Param("id") int id);

@Insert

    @Insert("insert into user(id,name,password) values (#{id},#{name},#{password})")
    int addUser(User user);

@Update

	@Update("Update user set name = #{name},password = #{password} where id = #{id}")
    int updateUserById( User user);

7. 多对一

以学生表和老师表为例,多名学生包含一位老师的tid;
利用ResultMap中的association标签,映射实体类中的对象属性

7.1 按查询嵌套处理(子查询)

  • 查询tid为1的老师拥有的所有学生的学生信息
<?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.ning.dao.StudentMapper">
	
	<!--映射结果集-->
	<resultMap id="studentMap" type="student">
		<result property="id" column="id"/>
		<result property="name" column="name"/>
		<!-- property对应着student实体类中的teacher对象,javaType为对应的实体类,select要连接查找的查询语句-->
		<association property="teacher" column="tid" javaType="Teacher" select="findTid"/>
	</resultMap>

	<!--查询student所有的信息,包括老师的信息,返回值为上面映射的结果集-->
	<select id="findAll" resultMap="studentMap">
		select * from student where tid = #{tid}
	</select>

	<select id="findTid" resultType="Teacher">
		select * from teacher
	</select>
	
</mapper>

运行测试

public class MapperTest {
    SqlSession sqlSession = MybatisUtils.getSession();
    TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

    @Test
    public void test(){
        List<Student> students = studentMapper.findAll(1);
        for (Student student : students) {
            System.out.println(student);
        }
        sqlSession.close();
    }
}

结果为

Student{id=1, name='小明', teacher=Teacher{id=1, name='秦老师'}}
Student{id=2, name='小红', teacher=Teacher{id=1, name='秦老师'}}
Student{id=3, name='小张', teacher=Teacher{id=1, name='秦老师'}}
Student{id=4, name='小李', teacher=Teacher{id=1, name='秦老师'}}
Student{id=5, name='小王', teacher=Teacher{id=1, name='秦老师'}}

7.2 按结果嵌套处理(联合查询)

<!--按结果嵌套处理-->
	<resultMap id="StudentTeacher2" type="Student">
		<id property="id" column="sid"/>
		<result property="name" column="sname"/>
		<!--关联对象property 关联对象在Student实体类中的属性-->
		<association property="teacher" javaType="Teacher">
			<result property="name" column="tname"/>
		</association>
	</resultMap>

	<select id="findAll2" resultMap="StudentTeacher2" >
		 select s.id sid, s.name sname , t.name tname from student s,teacher t where s.tid = t.id
	</select>

8. 一对多

一名老师包含了多个学生,首先在实体类中,学生不是个体的存在而是一个集合

Teacher实体类

public class Teacher {
    private Integer id;
    private String name;
    private List<Student> students;
}

8.1 按查询嵌套处理(子查询)

	<resultMap id="teacherMap2" type="com.ning.pojo.Teacher">
		<collection property="students" ofType="Student" select="findStu" column="id"/>
	</resultMap>

	<select id="findTeaById" resultMap="teacherMap2">
		select * from teacher where id = #{tid}
	</select>

	<!-- 这里的tid是从上面查老师那边传过来的 -->
	<select id="findStu" resultType="Student">
		select * from student where tid = #{tid}
	</select>

8.2 按结果嵌套处理(联合查询)

		<!--一对多 集合,使用collection-->
	<!--    JavaType和ofType都是用来指定对象类型的
			JavaType是用来指定pojo中属性的类型
			ofType指定的是映射到list集合属性中pojo的类型
	-->
	<resultMap id="teacherMap" type="com.ning.pojo.Teacher">
		<result column="tname" property="name"/>
		<result column="id" property="id"/>
		<collection property="students" ofType="Student">
			<result column="sid" property="id"/>
			<result column="sname" property="name"/>
			<result column="tid" property="tid"/>
		</collection>
	</resultMap>
	
	<select id="findStu" resultMap="teacherMap">
		select s.id sid ,s.name sname,t.name tname from teacher t,student s where t.id = s.tid
	</select>

运行结果

Teacher{id=null, name='秦老师', 
students=[Student{id=1, name='小明'}, 
Student{id=2, name='小红'}, 
Student{id=3, name='小张'}, 
Student{id=4, name='小李'},
Student{id=5, name='小王'}]}

9. 多对一、一对多(不同点)

  1. 关联-association
  2. 集合-collection
  3. 所以association是用于一对一和多对一,而collection是用于一对多的关系
  4. JavaType和ofType都是用来指定对象类型的
    JavaType是用来指定pojo中属性的类型
    ofType指定的是映射到list集合属性中pojo的类型。

10. 动态SQL

根据不同的条件生产不同的sql语句

10.1 < where > < if > 多条件判断

	<select id="findBlog" resultType="Blog" parameterType="com.ning.pojo.Blog">
		select * from blog
		<where>
		<!--如果blog类中的title属性不为空则去进行条件查询-->
		<if test="title!=null">
			title=#{title}
		</if>
		<!--如果这个条件也成立就进行拼接-->
		<if test="author!=null">
			and author=#{author}
		</if>
		</where>
	</select>

10.2 < choose > < when > < otherwise > 多条件判断

	<select id="findBlog" parameterType="com.ning.pojo.Blog" resultType="Blog">
		select * from blog
		<where>
			<choose>
				<!--如果条件成立则拼接-->
				<when test="title!=null">
					title=#{title}
				</when>
				<!--从第二个拼接开始都要加 and -->
				<when test="author!=null">
					and author=#{author}
				</when>
				<!--如果以上条件都不成立,则进入-->
				<otherwise>
					and views=#{views}
				</otherwise>
			</choose>
		</where>
	</select>

10.3 < set > update使用

	<update id="updateBlog" parameterType="com.ning.pojo.Blog">
		update blog
		<set>
			<if test="title!=null">
				title=#{title},
			</if>
			<if test="author!=null">
				author=#{author}
			</if>
		</set>
		where id=#{id}
	</update>

10.4 < sql > < include > 复用代码

抽取重复的代码,类似封装

	<!--把重复的if判断封装起来-->
	<sql id="if-title-author">
		<if test="title!=null">
			title=#{title}
		</if>
		<if test="author!=null">
			and author=#{author}
		</if>
	</sql>
	
	<select id="findBlog1" resultType="Blog" parameterType="com.ning.pojo.Blog">
		select * from blog
		<where>
		<!--这里只需要导入上面的id就可以-->
		<include refid="if-title-author"/>
		</where>
	</select>

10.5 < foreach >批量处理

	<select id="findBlogInId" parameterType="list" resultType="Blog">
		select * from blog
		<where>
			id in
			<!--collection 为对应的集合名称-->
			<foreach collection="list" item="id" open="(" separator="," close=")">
				#{id}
			</foreach>
		</where>
	</select>

11. 缓存

缓存只适用于经常查询,而不频繁改动的数据

11.1 一级缓存

  • 一级缓存也叫本地缓存:SqlSession

    • 与数据库同一次会话期间查询到的数据会放在本地缓存中

    • 以后如果需要获取相同的数据,直接从缓存中拿不需要再去查询数据库

11.1.1 四种导致一级缓存失效的情况

  • 不是同一个SqlSession
  • SqlSession相同,但是查询条件不同
  • SqlSession相同,两次查询中间有增删改操作
  • SqlSession相同,两次查询中间清空了一级缓存

11.2 二级缓存

  • 二级缓存也叫全局缓存
    • 基于namespace级别的缓存,一个名称空间对应着一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会放在当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了
    • 如果会话关闭时,一级缓存的数据被保持在二级缓存中,新的会话进来也可以从二级缓存中获取到数据

11.2.1 使用步骤

  1. 在mybatis-conifg.xml中加入设置,开启全局缓存
	<setting name="cacheEnabled" value="true"/>
  1. 在每个mapper.xml中配置二级缓存
	<!--开启二级缓存-->
	<cache/>

	<!--更细节的配置,开启二级缓存的同时,创建了一个 FIFO 缓存,每隔 60 秒刷新,
	最多可以存储结果对象或列表的 512 个引用,
	而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者 产生冲突。-->
	<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、付费专栏及课程。

余额充值