简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
环境搭建
导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
编写配置文件
resources/dao/SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis环境-->
<environments default="mysql">
<environment id="mysql">
<!--配置事务和数据源-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,指每个dao独立的配置文件-->
<mappers>
<mapper resource="dao/UserDao.xml"/>
</mappers>
</configuration>
properties标签的使用
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="mysqlcfg.properties"/>
<environments default="mysql">
<environment id="mysql">
<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="mapper/UserMapper.xml"/>
</mappers>
</configuration>
typeAliases标签
SqlMapperCfg.xml
<typeAliases>
<!--type 全类名, alias 别名,不区分大小写-->
<typeAlias type="domain.User" alias="user"/>
<!-- 扫描包下的全部实体类 -->
<package name="domain"/>
</typeAliases>
编写实体类
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
编写Dao接口
public interface UserDao {
//使用注解
//@Select("select * from user")
List<User> findAll();
}
resources/dao/UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDao">
<!--配置查询所有-->
<select id="findAll" resultType="entity.User">
select * from user
</select>
</mapper>
编写测试类
@Test
public void test1() throws Exception {
//1、读取配置文件
InputStream in = Resources.getResourceAsStream("dao/SqlMapConfig.xml");
//2、创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3、使用工厂生产SqlSession对象
SqlSession session = factory.openSession();
//4、使用SqlSession创建Dao接口的代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5、使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6、释放资源
session.close();
in.close();
}
CRUD操作
resources/mapper/userMapper.xml
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">
事务自动提交
session = factory.openSession(true);
删除操作
<delete id="deleteById" parameterType="integer">
delete from user
where id = #{id};
</delete>
修改操作
<update id="updateUser" parameterType="domain.User">
update user
set username=#{username},
birthday=#{birthday},
sex=#{sex},
address=#{address}
where id = #{id};
</update>
增加操作
<insert id="saveUser" parameterType="domain.User">
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username, birthday, sex, address)
values (#{username}, #{birthday}, #{sex}, #{address});
</insert>
查询操作
查询所有
<select id="findAll" resultType="domain.User">
select * from user;
</select>
通过ID查询
<select id="getUser" resultType="domain.User" parameterType="integer">
select * from user where id = #{id};
</select>
模糊查询
<select id="getUserByName" resultType="domain.User" parameterType="string">
select * from user
where username like #{username};
</select>
聚合函数
<select id="getUserCount" resultType="java.lang.Integer">
select count(*) from user;
</select>
通过对象查询
<select id="getUserByQueryVo" resultType="domain.User" parameterType="domain.QueryVo">
select * from user where username like #{user.username};
</select>
封装结果集
实体类成员变量名和数据库列名不一致的解决办法
方案一:取别名
select id as userId,username as userName from user where id=#{id}
方案二:配置resultMap
<resultMap id="userMap" type="domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</resultMap>
<select id="findAll" resultMap="userMap">
select *
from user;
</select>
动态Sql
if标签
<select id="getUserByUsername" resultType="domain.User">
select * from user where 1=1
<if test="username!=null">
and username like #{username}
</if>
<if test="sex!=null">
and sex=#{sex}
</if>
</select>
where标签
<select id="getUserByUsername" resultType="domain.User">
select * from user
<where>
<if test="username!=null">
and username like #{username}
</if>
<if test="sex!=null">
and sex=#{sex}
</if>
</where>
</select>
foreach标签
<sql id="defaultUser">
select * from user
</sql>
<select id="getUserByVo" resultType="domain.User" parameterType="domain.QueryVo">
<include refid="defaultUser"/>
<where>
<if test="ids!=null and ids.size()>0">
<foreach collection="ids" open="id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
多表查询
一对一(多对一)查询
一个人可以有一个或多个账户,一个账户或多个账户可以只属于一个人
方式一:重新定义一个PO类接受查询返回的信息 (常用)
public class UserAccount {
private int uid;
private int id;
private String username;
private double money;
private String address;
}
<select id="getAllAccount" resultType="domain.UserAccount">
select user.username, account.*, user.address
from account, user
where user.id = account.UID
</select>
方式二:用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
public class Account implements Serializable {
private int id;
private int uid;
private double money;
private User user;
}
@Test
public void testGetAllAccountByRsMap() {
List<Account> list = accountMapper.getAllAccountByResMap();
for (Account account : list){
System.out.println(account);
}
}
<!--定义封装User和Account的resultMap-->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!--封装User-->
<association property="user" column="uid" javaType="user">
<id property="id" column="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
</association>
</resultMap>
<select id="getAccount" resultMap="accountUserMap">
</select>
<select id="getAllAccountByResMap" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id
</select>
一对多
在User中封装List
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 一对多关系映射
private List<Account> accounts;
}
<!--定义封装User和Account的resultMap-->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!--封装User-->
<association property="user" column="uid" javaType="user">
<id property="id" column="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
</association>
</resultMap>
<select id="getAllAccountByResMap" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;
</select>
多对多
使用一张中间表使两张表建立联系,通过两次左外连接查询结果
@Data
public class Role {
private Integer id;
private String roleName;
private String roleDesc;
private List<User> userList;
}
<?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="mapper.RoleMapper">
<resultMap id="roleMap" type="domain.Role">
<id property="id" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<collection property="userList" ofType="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
</collection>
</resultMap>
<select id="findAll" resultMap="roleMap">
select u.*, r.id as rid, r.role_name, r.role_desc
from role r
left outer join user_role ur on r.id = ur.uid
left outer join user u on ur.uid = u.id;
</select>
</mapper>
延迟加载
问题
在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。
多对一,一对一:通常情况下我们都是采用立即加载。
解决方案
在sqlMapConfig.xml中开启延迟加载
<!--配置参数-->
<settings>
<!--开启Mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
定义resultMap
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容
select属性指定的内容:查询用户的唯一标识:
column属性指定的内容:用户根据id查询时,所需要的参数的值
-->
<association property="user" javaType="user" column="uid"
select="mapper.UserMapper.findById"></association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
<!-- 根据用户id查询账户列表 -->
<select id="findAccountByUid" resultType="account">
select * from account where uid = #{uid}
</select>
缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
一级缓存
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
二级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步:让当前的映射文件支持二级缓存(在UserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置) selectCache="true"
注解开发
环境搭建
配置文件
sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="mysql.properties"/>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<!--type 全类名, alias 别名,不区分大小写-->
<typeAlias type="entity.User" alias="user"/>
<!-- 扫描包下的全部实体类 -->
<!-- <package name="entity"/>-->
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<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>
<package name="dao"/>
</mappers>
</configuration>
实体类和Dao接口
@Data
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
public interface UserDao {
@Select("select * from user")
List<User> findAll();
}
测试类
public class Test1 {
private InputStream in;
private UserDao userDao;
private SqlSession sqlSession;
@Before
public void before() throws Exception {
in = Resources.getResourceAsStream("sqlMapCfg.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void after() throws Exception {
in.close();
sqlSession.close();
}
@Test
public void testSelect() {
List<User> userList = userDao.findAll();
for (User user : userList) {
System.out.println(user);
}
}
}
单表CRUD操作(代理Dao方式)
@Select("select * from user")
List<User> findAll();
@Insert(" insert into user(username, birthday, sex, address)" +
" values (#{username}, #{birthday}, #{sex}, #{address});")
void insertUser(User user);
@Update("update user set username=#{username} where id=#{id}")
void updateUser(User user);
@Delete("delete from user where id=#{id}")
void deleteUser(Integer id);
多表查询操作
使用注解配置resultMap
@Select("select * from user")
@Results(id="userMap",value = {
@Result(id = true,column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "sex", property = "sex"),
@Result(column = "birthday", property = "birthday"),
@Result(column = "address", property = "address")
})
List<User> findAll();
使用resultMap
@Select("select * from user where id=#{id}")
@ResultMap("userMap")
User findUserById(Integer id);
一对一(多对一)
建立账户与用户的一对一映射关系
public class Account {
private Integer id;
private Integer uid;
private Double money;
private User user;
}
DAO代码,使用@One封装一对一的对象
@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(column = "uid", property = "user",
one = @One(select = "dao.UserDao.findUserById", fetchType = FetchType.EAGER))
})
List<Account> findAll();
测试代码
public void testFindAll() {
List<Account> accountList = accountDao.findAll();
for (Account account : accountList) {
System.out.println(account);
System.out.println(account.getUser());
System.out.println("-----------------------------");
}
}
多对一
建立用户与账户的一对多映射关系
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accountList;
}
DAO代码,使用@One封装一对多的对象
@Select("select * from user")
@Results(id="userMap",value = {
@Result(id = true,column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "sex", property = "sex"),
@Result(column = "birthday", property = "birthday"),
@Result(column = "address", property = "address"),
@Result(column = "id", property = "accountList",
many = @Many(select = "dao.AccountDao.findAccountById", fetchType=FetchType.LAZY))
})
List<User> findAll();
测试代码
@Test
public void testSelect() {
List<User> userList = userDao.findAll();
for (User user : userList) {
System.out.println(user);
for (Account account : user.getAccountList()) {
System.out.println(account);
}
System.out.println("==================");
}
}
缓存配置
开启二级缓存:
在Dao接口中添加注解:@CacheNamespace(blocking = true)