Mybatis学习笔记

主要是看视频学习之后,做个笔记,可能有错误的地方

1 三层架构

表现层:用于展示数据

业务层:用于处理业务需求

持久层:用于和数据库交互

2 Mybatis介绍

mybatis是一个持久层框架,内部封装了JDBC,开发者只需要关注sql语句本身即可

开发者可以使用xml配置或者注解的方法配置各种statement

采用ORM思想(Object Relation Mapping 对象关系映射),将数据库表与实体类一一对应,开发者操作实体类即可实现操作数据库表

mybatis的dao实现方式

  1. 可以采用xml配置,mybatis自动实现dao实现类
  2. 可以采用注解配置,mybatis自动实现dao实现类
  3. mybatis也支持开发者自己写dao实现类

3 Maven中配置Mybatis

mybatis的版本号可以在mybatis的官网查询

<dependencies>
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.5.6</version>
	</dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
	</dependency>
</dependencies>

4 主配置文件SqlMapConfig.xml

在resources目录下创建SqlMapConfig.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>

4.1 配置environments环境

<environments default="mysql">
	<!-- 配置mysql的环境 -->
	<environment id="mysql">
	<!-- 配置事务的类型 -->
    <transactionManager type="JDBC"/>
	<!-- 配置数据源(连接池) -->
	<dataSource type="POOLED">
		<property name="driver" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://localhost:3306/java_study_mybatis"/>
		<property name="username" value="root"/>
		<property name="password" value=""/>
	</dataSource>
	</environment>
</environments>

4.2 配置mappers映射

如果采用xml配置方式,使用resource属性表示xml文件路径

<mappers>
	<mapper resource="com/zhq/study/mybatis/dao/IUserDao.xml"></mapper>
</mappers>

如果采用注解方法,使用class表示dao接口类的全限定类名

<mappers>
	<mapper class="com.zhq.study.mybatis.dao.IUserDao"/>
</mappers>

指定dao接口所在的包,当指定了之后,就不需要再写mapper以及resource或者class

<mappers>
	<package name="com.zhq.study.mybatis.dao"/>
</mappers>

4.3 配置properties标签

<properties>
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/java_study_mybatis"/>
    <property name="username" value="root"/>
    <property name="password" value=""/>
</properties>

也可以使用外部文件jdbcConfig.properties直接引入

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/eesy_mybatis
username=root
password=1234
<properties resource="jdbcConfig.properties"></properties>

注意事项

  1. resource属性:指定配置文件位置,按照类路径写法
  2. url属性:也是指定配置文件位置,按照url路径的写法

配置数据库的时候直接可以使用name

<property name="driver" value="${driver}"/>

4.4 配置typeAliases别名

<typeAliases>
    <!-- typeAlias用于配置别名,后面再引用的时候,就不需要实体类的全限定类名,只需要别名即可,别名不区分大小写 -->
    <typeAlias type="com.zhq.study.mybatis.domain.User" alias="aaauser"></typeAlias>

    <!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,类名就是别名,同样不区分大小写 -->
    <package name="com.zhq.study.mybatis.domain"/>
</typeAliases>

4.5 开启延迟加载

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

4.6 开启缓存

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

5 domain实体类

public void User implements Serializable {
	...
}

注意事项

  1. 属性名称最好与数据库表中的名称相同(windows中mysql不区分大小写)即可直接映射,否则需要另外配置名称映射
  2. 生成getter()和setter()方法
  3. 生成toString()方法

6 dao实现方式

6.1 XML配置

dao接口持久层

public interface IUserDao {
	...
    List<User> findAll();
    
    void saveUser(User user);
    
    void updateUser(User user);
    
    void deleteUser(Integer userId);
    
    User findById(Integer userId);
    
    List<User> findByName(String username);
    
    int findTotal();
    ...
}

在resources目录下,以与dao接口类相同的目录,创建IUserDao.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">

对应的sql语句:

<mapper namespace="com.zhq.study.mybatis.dao.IUserDao">
    <!-- 配置所有查询 -->
    <select id="findAll" resultType="com.zhq.study.mybatis.domain.User">
        select * from user;
    </select>
        
    <!-- 保存用户 -->
    <insert id="saveUser" parameterType="com.zhq.study.mybatis.domain.User">
		<!-- 配置插入操作后,获取插入数据的id -->
		<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
			select last_insert_id();
		</selectKey>
		insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday});
    </insert>
        
    <!-- 更新用户 -->
    <update id="updateUser" parameterType="com.zhq.study.mybatis.domain.User">
        update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id};
    </update>
        
    <!-- 删除用户 -->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        <!-- 只有一个参数,所以用占位符就可以 -->
        delete from user where id=#{uid};
    </delete>
        
    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="java.lang.Integer" resultType="com.zhq.study.mybatis.domain.User">
        select * from user where id=#{uid};
    </select>
        
    <!-- 根据名称模糊查询 -->
    <select id="findByName" parameterType="string" resultType="com.zhq.study.mybatis.domain.User">
        select * from user where username like #{name};
        <!-- select * from user where username like '%${value}%'; 必须要写成value-->
    </select>
            
    <!-- 获取用户总条数 -->
    <select id="findTotal" resultType="int">
        select count(id) from user;
    </select>
</mapper>

注意事项

  1. mapper标签的namespace属性:对应dao接口类的全限定类名
  2. id属性:对应方法名
  3. parameterType属性:该方法的参数类型的全限定类名(如果是基本类型,则可以直接使用)
  4. resultType属性:该方法的返回值类型的全限定类名
  5. #{username}中的参数应该与实体类的属性名称相同,如果只有一个参数则占位符即可
  6. 模糊查询时,如果采用#{name}则调用的时候需要加上%,如果采用’%${value}%'则必须写value

如果实体类属性和数据库表字段名称不相同,则可以在IUserDao.xml中另外配置名称映射

<resultMap id="userMap" type="com.zhq.study.mybatis.domain.User">
    <!-- 主键字段的对应 -->
    <id property="userId" column="id"></id>
    <!-- 非主键字段的对应 -->
    <result property="userName" column="username"></result>
    <result property="userAddress" column="address"></result>
    <result property="userSex" column="sex"></result>
    <result property="userBirthday" column="birthday"></result>
</resultMap>

<!-- 当使用了上面这种对应映射方法时,需要用resultMap,而不是resultType -->
<select id="findAll" resultMap="userMap">
	select * from user;
</select>

注意事项

  1. 主键字段用id标签,非主键字段用result标签
  2. property属性:实体类属性名称
  3. column属性:数据库表字段名称,或者是数据库语句查询结果的字段名称
  4. 当使用了名称映射配置之后,不再使用resultType,而用resultMap

6.2 注解

不需要xml文件配置,dao接口持久层

public interface IUserDao {
	...
    @Select("select * from user;")
    List<User> findAll();
    
    @Insert("insert into user(username, address, sex, birthday) values(#{username},#{address},#{sex},#{birthday})")
  	void saveUser(User user);

  	@Update("update user set username=#{username}, sex=#{sex}, address=#{address}, birthday=#{birthday} where id=#{id}")
  	void updateUser(User user);

  	@Delete("delete from user where id=#{id}")
  	void deleteUser(Integer id);

  	@Select("select * from user where id=#{id}")
  	User findById(Integer userId);

	@Select("select * from user where username like #{username}")
  	List<User> findUserByName(String username);

  	@Select("select count(*) from user")
  	int findTotalUser();
	...
}

如果实体类属性和数据库表字段名称不相同,则可以在注解上加Results

@Select("select * from account")
@Results(id = "accountMap", value = {
      @Result(id = true, column = "id", property = "id"),
      @Result(column = "uid", property = "uid"),
      @Result(column = "money", property = "money")})
List<Account> findAll();

@Select("select * from account where uid=#{id}")
@ResultMap(value = {"accountMap"})
List<Account> findAllByUid(Integer id);

注意事项

  1. Results标签的id属性:提供一个id,在其他方法中可以直接使用注解ResultMap引用
  2. Result标签的id属性:表示该实体类属性是否是主键

6.3 自己写dao

mybatis也支持自己写dao实现

public class UserDaoImpl implements IUserDao {

	private SqlSessionFactory factory;

	public UserDaoImpl(SqlSessionFactory factory) {
		this.factory = factory;
	}

	public List<User> findAll() {
		// 1. 根据factory生成SqlSession对象
		SqlSession session = factory.openSession();
    	// 2. 调用SqlSession中的方法,实现查询列表
    	List<User> users = session.selectList("com.zhq.study.mybatis.dao.IUserDao.findAll"); // 参数就是配置文件中的key
    	// 3. 释放资源
    	session.close();
    	return users;
	}
    
    ...
    session.insert("com.zhq.study.mybatis.dao.IUserDao.saveUser", user);
    ...
    session.update("com.zhq.study.mybatis.dao.IUserDao.updateUser", user);
    ...
    session.delete("com.zhq.study.mybatis.dao.IUserDao.deleteUser", userId);
    ...
    User user = session.selectOne("com.zhq.study.mybatis.dao.IUserDao.findById", userId);
    ...
}

注意事项

  1. session调用方法的参数就是对应要执行的dao接口方法在配置文件中的key

7 进阶操作

7.1 多条件组合查询

在domain中定义一个查询对象类QueryVo,将多个条件封装到一个实体中作为接口方法的参数

public class QueryVo {

	private User user;
    
    private List<Integer> ids;

	...
}

IUserDao中定义查询语句

List<User> findUserByVo(QueryVo vo);

List<User> findUserByids(QueryVo vo);

IUserDao.xml中配置sql语句

<select id="findUserByVo" parameterType="com.zhq.study.mybatis.domain.QueryVo" resultType="com.zhq.study.mybatis.domain.User">
	select * from user where username like #{user.username};
</select>

7.2 xml中其他标签

<!-- 用where语句 -->
<select id="findUserByCondition" resultType="com.zhq.study.mybatis.domain.User" parameterType="com.zhq.study.mybatis.domain.User">
    select * from user
    <where>
        <if test="username != null">
            and username=#{username}
        </if>
        <if test="sex != null">
            and sex=#{sex}
        </if>
    </where>
</select>

<!-- 根据queryvo中的id集合实现查询用户列表 -->
<select id="findUserByids" resultType="com.zhq.study.mybatis.domain.User" parameterType="com.zhq.study.mybatis.domain.QueryVo">
    select * from user
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
                #{uid}
            </foreach>
        </if>
    </where>
</select>

7.3 多表查询:一对一

例子:一个用户可以有多个账户,每个账户只属于一个用户

实体类,从表实体应该包含一个主表实体的对象引用

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    // 从表实体应该包含一个主表实体的对象引用
    private User user;
}

如果采用xml配置

<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="com.zhq.study.mybatis.domain.Account">
    <id property="id" column="aid"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 建立一对一的关系映射,配置封装user的内容 -->
    <association property="user" column="uid">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <result property="birthday" column="birthday"></result>
    </association>
</resultMap>

<!-- 配置所有查询 -->
<select id="findAll" resultMap="accountUserMap">
    select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id=a.uid;
</select>

也可以通过另一个查询方法

<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="com.zhq.study.mybatis.domain.Account">
    <id property="id" column="id"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 建立一对一的关系映射,配置封装user的内容 -->
    <association property="user" column="uid" select="com.zhq.study.mybatis.dao.IUserDao.findById"/>
</resultMap>

如果采用注解方式

public interface IAccountDao {

  	/* 查询所有账户,并获取对应的用户 */
  	@Select("select * from account")
  	@Results(id = "accountMap", value = {
          	@Result(id = true, column = "id", property = "id"),
          	@Result(column = "uid", property = "uid"),
          	@Result(column = "money", property = "money"),
          	@Result(property = "user", column = "uid",
                  	one = @One(select = "com.zhq.study.mybatis.dao.IUserDao.findById", fetchType = FetchType.EAGER))})
  	List<Account> findAll();
}

注意事项

  1. one属性:表示一对一
  2. column属性:表示用什么名称去匹配property,或用哪个属性作为查询参数
  3. select属性:表示需要连着查询的方法
  4. fetchType属性:立即加载或者延迟加载

7.4 多表查询:一对多

例子:一个用户可以有多个账户,每个账户只属于一个用户

实体类,主表实体应该包含从表实体的映射

public class User implements Serializable {

    /* 确保属性名称跟数据库一样,mysql数据库在windows上不区分大小写 */
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一对多的关系映射,主表实体应该包含从表实体的映射
    private List<Account> accounts;
}

如果采用xml配置

<!-- 定义User的resultMap,做一对多的映射 -->
<resultMap id="userAccountMap" type="com.zhq.study.mybatis.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="address" column="address"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <!-- 配置user对象中的account集合,建立一对多关系 -->
    <collection property="accounts" ofType="com.zhq.study.mybatis.domain.Account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
    </collection>
</resultMap>

<!-- 配置所有查询 -->
<select id="findAll" resultMap="userAccountMap">
    select * from user u left outer join account a on u.id=a.uid;
</select>

也可以通过另一个查询方法

<!-- 定义User的resultMap,做一对多的映射 -->
<resultMap id="userAccountMap" type="com.zhq.study.mybatis.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="address" column="address"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <!-- 配置user对象中的account集合,建立一对多关系 -->
    <collection property="accounts" ofType="com.zhq.study.mybatis.domain.Account"
                select="com.zhq.study.mybatis.dao.IAccountDao.findAllByUid" column="id"/>
</resultMap>

如果采用注解方式

public interface IUserDao {

  	/* 查询所有用户*/
  	/* 如果实体类的名称和数据库不对应,使用Results注解解决 */
  	@Select("select * from user")
  	@Results(id = "userMap", value = {
          	@Result(id = true, column = "id", property = "id"),
          	@Result(column = "username", property = "username"),
          	@Result(column = "address", property = "address"),
          	@Result(column = "sex", property = "sex"),
          	@Result(column = "birthday", property = "birthday"),
          	@Result(property = "accounts", column = "id",
                  	many = @Many(select = "com.zhq.study.mybatis.dao.IAccountDao.findAllByUid", fetchType = FetchType.LAZY))
  		})
      List<User> findAll();
}

注意事项

  1. many属性:表示一对多
  2. column属性:表示用什么名称去匹配property,或用哪个属性作为查询参数
  3. select属性:表示需要连着查询的方法
  4. fetchType属性:立即加载或者延迟加载

7.5 多表查询:多对多

例子:一个用户可以是多个角色,一个角色也可以属于多个用户

多对多需要一张中间表作为连接

配置同一对多

7.6 延迟加载与立即加载

一般来说,一对多,加载账户的时候需要同时加载用户信息(立即加载),加载用户的时候不必要同时加载账户信息(延迟加载)

延迟加载是利用另一个查询方法,做到在需要结果的时候再查询

主配置文件

<settings>
    <!-- 开启mybatis支持延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

一对一

<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="com.zhq.study.mybatis.domain.Account">
    <id property="id" column="id"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 建立一对一的关系映射,配置封装user的内容 -->
    <!-- 延迟加载,select用于指定的内容:查询用户的唯一标识 -->
    <!-- 延迟加载需要在主配置文件配置 -->
    <association property="user" column="uid" select="com.zhq.study.mybatis.dao.IUserDao.findById"/>
</resultMap>

多对多

<!-- 定义User的resultMap,做一对多的映射 -->
<resultMap id="userAccountMap" type="com.zhq.study.mybatis.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="address" column="address"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <!-- 配置user对象中的account集合,建立一对多关系 -->
    <!-- 延迟加载,使用select指定加载的内容 -->
    <collection property="accounts" ofType="com.zhq.study.mybatis.domain.Account"
                select="com.zhq.study.mybatis.dao.IAccountDao.findAllByUid" column="id"/>
</resultMap>

注意事项

  1. select属性:指定需要延迟加载调用的查询方法
  2. column属性:传入延迟加载查询方法的参数字段
  3. ofType属性:因为实体类属性是集合,需要指定集合元素的类型

7.7 缓存

适用于缓存的数据:经常查询、不经常改变、数据的正确与否对最终结果影响不大

不适用于缓存的数据:经常改变的数据、数据的正确与否对最终结果影响很大

7.7.1 一级缓存

一级缓存是SqlSession对象的缓存,执行查询之后,查询结果会同时存入SqlSession提供的缓存区域中,该区域的结构是一个map,当再次查询同样的数据时,mybatis会首先去SqlSession缓存当中查询

当调用修改、添加、删除、commit()、close()方法时,一级缓存就清空了

mybatis默认开启一级缓存

7.7.2 二级缓存

二级缓存是SqlSessionFactory对象的缓存

主配置文件

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

如果采用xml配置

<cache/>

<select id="findAll" resultMap="userAccountMap" useCache="true">
    select * from user;
</select>

如果采用注解方式

@CacheNamespace(blocking = true)
public interface IUserDao {
	...
}

8 Mybatis分析

8.1 代理

mybatis中,xml配置或者注解都是使用代理dao的方式实现dao的功能

在代理dao中,调用selectList方法

selectList方法中,根据配置文件注册连接驱动,获取sql语句创建预处理对象PreparedStatement,执行sql语句,使用反射机制将结果封装到对象中

8.2 连接池

连接池是用于存储连接的一个容器,该容器是线程安全的集合,具有先进先出特性

在主配置文件中数据源的type属性,除了POOLED,还可以是UNPOOLED或者JNDI

UNPOOLED:采用传统获取连接的方式,需要的时候创建连接

JNDI:采用服务器提供的JNDI技术实现,不同服务器拿到DataSource是不一样的,必须使用web或者maven的war工程才能使用

9 如何调用运行

	// 1. 读取配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
	// 2. 创建SqlSessionFactory工厂
	// 使用了构建者模式,优势:把对象的创建细节隐藏
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	// 3. 使用工厂生产SqlSession对象
	// 使用了工厂模式,优势:解耦,降低类之间的依赖关系
    SqlSession session = factory.openSession();
	// 4. 使用SqlSession创建Dao接口的代理对象
	// 使用了代理模式,优势:不修改源码的基础上对已有方法增强
    IUserDao userDao = session.getMapper(IUserDao.class);
	// 5. 使用代理对象执行方法
    List<User> users = userDao.findAll();
    for(User user: users){
      System.out.println(user);
    }
    // 6. 提交事务(如果需要的话)
    session.commit();
	// 7. 释放资源
    session.close();
    in.close();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值