alohaaukake

简介

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,在AccountMapper.xml中使用resultMap建立映射

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)

分类:

java

技术点:

相关文章: