5/10day51_查詢&多表

mybatis查詢&多表.png

回顧

1. 創(chuàng)建java工程,導入相關(guān)jar包

2. 根據(jù)user表編寫User實體類

3. 編寫UserMapper接口

4. 編寫UserMapper.xml映射文件

5. 復制SqlMapConfig.xml (mybatis-config.xml)

6. 測試查詢...
1589073275902.png

優(yōu)化測試方法

public class UserMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在測試方法執(zhí)行之前,執(zhí)行
    @Before
    public void before() {
        // 獲取sqlSession對象
        sqlSession = MyBatisUtils.openSession();// 此方法必須線程內(nèi)獨享....
    }

    // 此方法在測試地方法執(zhí)行之后,執(zhí)行
    @After
    public void after() {
        // 關(guān)閉sqlSession
        MyBatisUtils.close(sqlSession);
    }

    @Test
    public void testFindAll() throws Exception {
        // 執(zhí)行sql
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.findAll();
        System.out.println(list);

    }
}

mybatis查詢和多表

1. 單表查詢【重點】

2. mapper映射文件深入講解【重點】

3. 回顧表關(guān)系【重點】

4. 多表查詢【重點】

一 Mybatis單表查詢

1.1 resultMap標簽

  • 如果數(shù)據(jù)庫返回結(jié)果的列名和要封裝的實體的屬性名完全一致的話用 resultType 屬性

  • 如果數(shù)據(jù)庫返回結(jié)果的列名和要封裝的實體的屬性名有不一致的情況用 resultMap 屬性

    • 使用resultMap==手動建立對象關(guān)系映射==。

① UserMapper接口

    // ResultMap標簽
    public List<User> findAllResultMap();

② UserMapper.xml

    <!--
        resultMap 手動建立映射
            id="userResultMap"
            type="cn.itcast.domain.User" 建立映射的java類型
        id 標簽 主鍵
            column="uid" 列名
            property="id" 實體屬性名
       result 標簽 普通字段
            column="name" 列名
            property="username" 實體屬性名
    -->
    <resultMap id="userResultMap" type="cn.itcast.domain.User">
        <id column="uid" property="id"></id>
        <result column="name" property="username"></result>
        <result column="bir" property="birthday"></result>
        <result column="gender" property="sex"></result>
        <result column="address" property="address"></result>
    </resultMap>

    <!--
        模擬表與實體的屬性名不一致情況
    -->
    <select id="findAllResultMap" resultMap="userResultMap">
        SELECT id AS uid, username AS `name`,birthday AS bir ,sex AS gender ,address FROM `user`
    </select>

③ 測試

    // resultMap標簽
    @Test
    public void testFindAllResultMap() throws Exception {
        // 獲取代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 執(zhí)行sql
        List<User> list = userMapper.findAllResultMap();
        for (User user : list) {
            System.out.println(user);
        }
    }

1.2 多條件查詢(二種)

需求

根據(jù)id和username查詢user表

① UserMapper接口

// 多條件查詢,方式一
public List<User> findByIdAndUsername1(@Param("id") Integer id, @Param("username") String username);

// 多條件查詢,方式二
public List<User> findByIdAndUsername2(User user);

② UserMapper.xml

<!--
        多條件查詢方式一
            如果傳遞多個參數(shù) parameterType屬性省略不寫...
    -->
<select id="findByIdAndUsername1"  resultType="cn.itcast.domain.User">
    select * from user where id = #{id} and username = #{username}
</select>

<!--
        多條件查詢方式二
            其實mybatis這哥們傳遞一個參數(shù) parameterType也可以省略【不太建議...】
    -->
<select id="findByIdAndUsername2" parameterType="cn.itcast.domain.User"  resultType="cn.itcast.domain.User">
    select * from user where id = #{id} and username = #{username}
</select>

③ 測試

// 多條件查詢
@Test
public void test01()throws Exception{
    // 獲取代理對象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 方式一
    /*List<User> list = userMapper.findByIdAndUsername1(41, "老王");
        System.out.println(list);*/

    // 方式二
    User user = new User();
    user.setId(41);
    user.setUsername("老王");
    List<User> list = userMapper.findByIdAndUsername2(user);
    System.out.println(list);
}

1.3 模糊查詢(四種)

需求

根據(jù)username模糊查詢user表

① UserMapper接口

    // 模糊查詢,方式一
    public List<User> findByUsername1(String username);

    // 模糊查詢,方式二
    public List<User> findByUsername2(String username);

    // 模糊查詢,方式三
    public List<User> findByUsername3(String username);

    // 模糊查詢,方式四
    public List<User> findByUsername4(String username);

② UserMapper.xml

    <!--
        模糊查詢,方式一
            java代碼與sql語句有耦合
    -->
    <select id="findByUsername1" parameterType="string" resultType="User">
        select * from user where username like #{username}
    </select>

    <!--
        模糊查詢,方式二【了解】
            mysql5.5版本之前,此拼接不支持多個單引號
            oracle數(shù)據(jù)庫,除了別名的位置,其余位置都不能使用雙引號
    -->
    <select id="findByUsername2" parameterType="string" resultType="User">
        select * from user where username like "%" #{username} "%"
    </select>

    <!--
        模糊查詢,方式三【此方式,會出現(xiàn)sql注入...】
            ${} 字符串拼接,如果接收的簡單數(shù)據(jù)類型,表達式名稱必須是value
    -->
    <select id="findByUsername3" parameterType="string" resultType="User">
        select * from user where username like '%${value}%'
    </select>

    <!--
        模糊查詢,方式四【掌握】
            使用concat()函數(shù)拼接
            注意:oracle數(shù)據(jù)庫 concat()函數(shù)只能傳遞二個參數(shù)...  可以使用函數(shù)嵌套來解決
    -->
    <select id="findByUsername4" parameterType="string" resultType="User">
        select * from user where username like concat(concat('%',#{username}),'%')
    </select>

③ 測試

    // 模糊查詢
    @Test
    public void test02()throws Exception{
        // 獲取代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 方式一
        // List<User> list = userMapper.findByUsername1("%王%");
        // 方式二
        // List<User> list = userMapper.findByUsername2("王");
        // 方式三
        // List<User> list = userMapper.findByUsername3("王");
        // 方式四
        List<User> list = userMapper.findByUsername4("王");
        System.out.println(list);
    }

1.4 ${} 與 #{} 區(qū)別【面試題】

${}:底層 Statement

  1. sql與參數(shù)拼接在一起,會出現(xiàn)sql注入問題
  2. 每次執(zhí)行sql語句都會編譯一次
  3. 接收簡單數(shù)據(jù)類型,命名:${value}
  4. 接收引用數(shù)據(jù)類型,命名: ${屬性名}
  5. 字符串類型需要加 '${value}'

org.apache.ibatis.scripting.xmltags.TextSqlNode

#{}:底層 PreparedStatement

  1. sql與參數(shù)分離,不會出現(xiàn)sql注入問題
  2. sql只需要編譯一次
  3. 接收簡單數(shù)據(jù)類型,命名:#{隨便寫}
  4. 接收引用數(shù)據(jù)類型,命名:#{屬性名}

二 Mybatis映射文件深入

環(huán)境搭建

1589078295166.png

2.1 返回主鍵

應(yīng)用場景

向數(shù)據(jù)庫保存一個user對象后, 然后在控制臺記錄下此新增user的主鍵值(id)

① UserMapper接口

public interface UserMapper {

    // 返回主鍵,方式一
    public void save1(User user);

    // 返回主鍵,方式二
    public void save2(User user);
}

② UserMapper.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="cn.itcast.dao.UserMapper">

    <!--
        返回主鍵,方式一 useGeneratedKeys屬性
            useGeneratedKeys="true" 開啟新增主鍵返回功能
            keyColumn="id"  user表中主鍵列
            keyProperty="id" user實體主鍵屬性

            注意:僅支持主鍵自增類型的數(shù)據(jù)庫 MySQL 和 SqlServer , oracle不支持

    -->
    <insert id="save1" parameterType="User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into user(username,birthday,sex,address)
          values(#{username},#{birthday},#{sex},#{address})
    </insert>

    <!--
        返回主鍵,方式二  <selectKey>
             keyColumn="id" user表中主鍵列
             keyProperty="id" user實體主鍵屬性
             resultType="int" user實體主鍵屬性類型
             order="AFTER"  表示此標簽內(nèi)部sql語句在insert執(zhí)行之前(執(zhí)行),還是之后執(zhí)行(執(zhí)行)
                AFTER 之后執(zhí)行【在自增主鍵時】
                BEFORE 之前執(zhí)行【使用指定主鍵時】

    -->
    <insert id="save2" parameterType="User" >
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into user(username,birthday,sex,address)
          values(#{username},#{birthday},#{sex},#{address})
    </insert>
</mapper>

③ 測試

public class UserMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在測試方法執(zhí)行之前,執(zhí)行
    @Before
    public void before() {
        // 獲取sqlSession對象
        sqlSession = MyBatisUtils.openSession();// 此方法必須線程內(nèi)獨享....
    }

    // 此方法在測試地方法執(zhí)行之后,執(zhí)行
    @After
    public void after() {
        // 關(guān)閉sqlSession
        MyBatisUtils.close(sqlSession);
    }

    // 返回主鍵
    @Test
    public void test01() throws Exception {
        // 獲取代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setUsername("xiao六");
        user.setBirthday(new Date());
        user.setAddress("中國");
        user.setSex("男");
        // 方式一
        // userMapper.save1(user);
        userMapper.save2(user);

        System.out.println("新增時,主鍵返回:" + user.getId());
    }
}

2.2 動態(tài)SQL

2.2.1 什么是動態(tài)SQL

需求

把id和username封裝到user對象中,將user對象中不為空的屬性作為查詢條件

1589022259909.png

這個時候我們執(zhí)行的sql就有多種可能

-- 如果id和用戶名不為空
select * from user where id= #{id} and username = #{username}

-- 如果只有id
select * from user where id= #{id} 

-- 如果只有用戶名
select * from user where username = #{username}

-- 如果id和用戶名都為空
select * from user

像上面這樣, 根據(jù)傳入的參數(shù)不同, 需要執(zhí)行的SQL的結(jié)構(gòu)就會不同,這就是動態(tài)SQL

2.2.2 if 條件判斷

需求

把id和username封裝到user對象中,將user對象中不為空的屬性作為查詢條件

① UserMapper接口

    // if 條件判斷
    public List<User> findByIdAndUsernameIf(User user);

② UserMapper.xml

    <!--
        if標簽 條件判斷
        where標簽  相當于 where 1=1 功能,如果沒有條件情況下 where語句不在sql語句拼接
            可以去掉第一個 and 或者 or
    -->
    <select id="findByIdAndUsernameIf" parameterType="User" resultType="User">
        select * from user
        <where>
            <if test="id != null">
                and id= #{id}
            </if>
            <if test="username !=null">
                and username = #{username}
            </if>
        </where>
    </select>

③ 測試

    // if判斷
    @Test
    public void test02()throws Exception{
        // 獲取代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 拼接條件
        User param = new User();
       // param.setId(41);
        // param.setUsername("老王");

        List<User> list = userMapper.findByIdAndUsernameIf(param);

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

2.2.3 set 用于update語句

需求

動態(tài)更新user表數(shù)據(jù),如果該屬性有值就更新,沒有值不做處理

① UserMapper接口

    // set 更新
    public void updateIf(User user);

② UserMapper.xml

<!--
        set標簽 更新 ,將條件中的最后一個逗號抹除
    -->
<update id="updateIf" parameterType="User">
    update user
    <set>
        <if test="username !=null">
            username = #{username} ,
        </if>
        <if test="birthday !=null">
            birthday = #{birthday} ,
        </if>
        <if test="sex !=null">
            sex = #{sex} ,
        </if>
        <if test="address != null">
            address = #{address},
        </if>
    </set>
    where id = #{id}
</update>

③ 測試

    @Test
    public void test03()throws Exception{
        // 獲取代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 設(shè)置更新內(nèi)容
        User user = new User();
        user.setId(50);
        user.setUsername("小四");

        userMapper.updateIf(user);
    }

2.2.4 foreach 用于循環(huán)遍歷【重點】

需求

根據(jù)多個id查詢,user對象的集合

select * from user where id in (41,43,46);
* <foreach>標簽用于遍歷集合,它的屬性:

    ? collection:代表要遍歷的集合元素

    ? open:代表語句的開始部分

    ? close:代表結(jié)束部分

    ? item:代表遍歷集合的每個元素,生成的變量名

    ? sperator:代表分隔符

練習三個版本

  1. 普通list集合
  2. 普通array數(shù)組
  3. 實體屬性list集合

① UserMapper

    // foreach標簽,普通list集合
    public List<User> findByList(List<Integer> ids);

    // foreach標簽,普通array數(shù)組
    public List<User> findByArray(Integer [] ids);

    // foreach標簽,實體屬性list集合
    public List<User> findByQueryVo(QueryVo queryVo);

② UserMapper.xml

    <!--
        foreach標簽,普通list集合
            傳遞 普通類型list集合   collection="list" 屬性取值:collection、list
    -->
    <select id="findByList" parameterType="list" resultType="User">
        select * from user where id in
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>


    <!--
        foreach標簽,普通array數(shù)組
            傳統(tǒng) 普通類型array數(shù)組  collection="array" 屬性取值 array
    -->
    <select id="findByArray" parameterType="int" resultType="User">
         select * from user where id in
         <foreach collection="array" open="(" close=")" item="id" separator=",">
             #{id}
         </foreach>
    </select>

    <!--
        foreach標簽,實體屬性list集合
            傳遞 實體中l(wèi)ist屬性集合的話,collection="ids" 取值,實體的屬性名
    -->
    <select id="findByQueryVo" parameterType="QueryVo" resultType="User">
         select * from user where id in
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>

③ 測試

    //  foreach標簽
    @Test
    public void test04()throws Exception{
        // 獲取代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 普通list集合
       /* List ids = new ArrayList();
        ids.add(41);
        ids.add(46);
        List list = userMapper.findByList(ids);*/
       // 普通array數(shù)組
      /* Integer[] ids = {41,46,49};
        List<User> list = userMapper.findByArray(ids);*/

      // 實體屬性list集合
        List ids = new ArrayList();
        ids.add(41);
        ids.add(46);
        QueryVo queryVo = new QueryVo();
        queryVo.setIds(ids);
        List<User> list = userMapper.findByQueryVo(queryVo);
        System.out.println(list);
    }

2.3 SQL片段

應(yīng)用場景

映射文件中可將重復的 sql 提取出來,使用時用 include 引用即可,最終達到 sql 重用的目的

    <!--
        foreach標簽,普通list集合
            傳遞 普通類型list集合   collection="list" 屬性取值:collection、list
    -->
    <select id="findByList" parameterType="list" resultType="User">
        <include refid="selectUser"></include> where id in
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>


    <!--
        foreach標簽,普通array數(shù)組
            傳統(tǒng) 普通類型array數(shù)組  collection="array" 屬性取值 array
    -->
    <select id="findByArray" parameterType="int" resultType="User">
        <include refid="selectUser"></include>  where id in
         <foreach collection="array" open="(" close=")" item="id" separator=",">
             #{id}
         </foreach>
    </select>

    <!--
        foreach標簽,實體屬性list集合
            傳遞 實體中l(wèi)ist屬性集合的話,collection="ids" 取值,實體的屬性名
    -->
    <select id="findByQueryVo" parameterType="QueryVo" resultType="User">
         <include refid="selectUser"></include> where id in
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>


    <!--
        將當前映射文件的共同的sql代碼抽取一個片段,實現(xiàn)sql的復用性...
         id="selectUser" 當前sql片段的唯一標識
    -->
    <sql id="selectUser">
         select id,username,birthday,sex,address from user
    </sql>

2.4 知識小結(jié)

MyBatis映射文件配置

<select>:查詢

<insert>:插入

<update>:修改

<delete>:刪除

<selectKey>:返回主鍵

<where>:where條件

<if>:if判斷

<foreach>:for循環(huán)

<set>:set設(shè)置

<sql>:sql片段抽取

三 表關(guān)系回顧

在關(guān)系型數(shù)據(jù)庫當中,表關(guān)系分為三種

* 特殊情況:
    一個訂單只能從屬于一個用戶,mybatis框架就把這個多對一看做成一對一來實現(xiàn)
    
* 數(shù)據(jù)建立表關(guān)系:通過主外鍵關(guān)聯(lián)

* 實體建立關(guān)系:通過屬性關(guān)聯(lián)
1589091271542.png

四 MyBatis多表查詢

1589091428909.png

環(huán)境搭建

1589092722809.png

4.1 一對一(多對一)

一對一查詢模型

用戶表和訂單表的關(guān)系為,一個用戶有多個訂單,一個訂單只從屬于一個用戶

一對一查詢的需求:查詢一個訂單,與此同時查詢出該訂單所屬的用戶

1589091522985.png

① 實體和表映射關(guān)系

SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.`id` = 1
1589092976533.png

② Order實體類

// 訂單實體類
public class Order {

    private Integer id;

    private Date ordertime;

    private Double money;

    // 一個訂單從屬于一個用戶
    private User user;
}

③ OrderMapper接口

public interface OrderMapper {

    // 一對一關(guān)聯(lián)查詢
    public Order findByIdWithUser(Integer id);
}

④ OrderMapper.xml

1589093842562.png
<?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="cn.itcast.dao.OrderMapper">


    <resultMap id="orderMap" type="cn.itcast.domain.Order">
        <id column="id" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="money" property="money"></result>
        <!--
            一對一多表關(guān)聯(lián) association標簽
                 property="user" 關(guān)聯(lián)實體的屬性名
                 javaType="cn.itcast.domain.User" 關(guān)聯(lián)實體java類型
        -->
        <association property="user" javaType="cn.itcast.domain.User">
            <id column="uid" property="id"></id>
            <result column="username" property="username"></result>
            <result column="birthday" property="birthday"></result>
            <result column="sex" property="sex"></result>
            <result column="address" property="address"></result>
        </association>
    </resultMap>


    <!--
        一對一關(guān)聯(lián)查詢
            resultType:單表映射封裝
            resultMap:多表查詢必須手動映射封裝
    -->
    <select id="findByIdWithUser" parameterType="int" resultMap="orderMap">
        SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.`id` = #{id}
    </select>
</mapper>

⑤ 測試

public class OrderMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在測試方法執(zhí)行之前,執(zhí)行
    @Before
    public void before() {
        // 獲取sqlSession對象
        sqlSession = MyBatisUtils.openSession();// 此方法必須線程內(nèi)獨享....
    }

    // 此方法在測試地方法執(zhí)行之后,執(zhí)行
    @After
    public void after() {
        // 關(guān)閉sqlSession
        MyBatisUtils.close(sqlSession);
    }


    // 一對一關(guān)聯(lián)測試
    @Test
    public void test01()throws Exception{
        // 獲取代理對象
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

        // 根據(jù)id查詢
        Order order = orderMapper.findByIdWithUser(1);
        System.out.println(order);
    }
}

4.2 一對多

一對多查詢模型

用戶表和訂單表的關(guān)系為,一個用戶有多個訂單,一個訂單只從屬于一個用戶

一對多查詢的需求:查詢一個用戶,與此同時查詢出該用戶具有的訂單

1589095235966.png

① 實體和表關(guān)系

SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id`=41
1589095492155.png

② User實體類

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一個用戶具有多個訂單
    private List<Order> orderList;
}

③ UserMapper接口

public interface UserMapper {

    // 一對多關(guān)聯(lián)
    public User findByIdWithOrders(Integer id);
}

④ UserMapper.xml

1589096423076.png
<?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="cn.itcast.dao.UserMapper">


    <resultMap id="userMap" type="cn.itcast.domain.User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <!--
            一對多關(guān)聯(lián) collection標簽
                property="orderList" 關(guān)聯(lián)實體集合的屬性名
                ofType="cn.itcast.domain.Order" 關(guān)聯(lián)實體的java類型(集合泛型的類型)
        -->
        <collection property="orderList" ofType="cn.itcast.domain.Order">
            <id column="oid" property="id"></id>
            <result column="ordertime" property="ordertime"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>

<!--
    一對多關(guān)聯(lián)
-->
    <select id="findByIdWithOrders" parameterType="int" resultMap="userMap">
       SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id`=#{id}
    </select>
</mapper>

⑤ 測試

1589096245643.png
public class UserMapperTest extends BaseMapperTest {

    // 一對多測試
    @Test
    public void test01() throws Exception {
        // 獲取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findByIdWithOrders(41);

        System.out.println(user);
    }
}

4.3 多對多(由二個一對多組成)

多對多查詢的模型

用戶表和角色表的關(guān)系為,一個用戶有多個角色,一個角色被多個用戶使用

多對多查詢的需求:查詢用戶同時查詢出該用戶的所有角色

在mybatis中多對多實現(xiàn),跟一對多步驟是一樣,區(qū)別就在于sql語句

1589096723649.png

① 實體和表關(guān)系

SELECT * FROM `user` u 
    INNER JOIN user_role ur ON u.`id` = ur.`uid`  -- 用戶連接中間表
    INNER JOIN role r ON ur.`rid` = r.`id`  -- 再根據(jù)中間表連接角色
    WHERE u.id = 41 -- 用戶id 作為條件
1589097185348.png

② User和Role實體

public class Role {

    private Integer id;

    private String roleName;

    private String roleDesc;
}
public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一個用戶具有多個角色
    private List<Role> roleList;
}

③ UserMapper接口

    // 多對多關(guān)聯(lián)
    public User findByIdWithRoles(Integer id);

④ UserMapper.xml

1589097841134.png
    <resultMap id="userWithRoleMap" type="cn.itcast.domain.User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <!--
            多對多實現(xiàn)步驟和一對多是一樣的(區(qū)別在于sql語句)
        -->
        <collection property="roleList" ofType="cn.itcast.domain.Role">
            <id column="rid" property="id"></id>
            <result column="role_name" property="roleName"></result>
            <result column="role_desc" property="roleDesc"></result>
        </collection>
    </resultMap>
    
    <!--
        多對多關(guān)聯(lián)
    -->
    <select id="findByIdWithRoles" parameterType="int" resultMap="userWithRoleMap">
        SELECT * FROM `user` u 
            INNER JOIN user_role ur ON u.`id` = ur.`uid`  -- 用戶連接中間表
            INNER JOIN role r ON ur.`rid` = r.`id`  -- 再根據(jù)中間表連接角色
            WHERE u.id = #{id} -- 用戶id 作為條件
    </select>

⑤ 測試

    // 多對多測試(根據(jù)用戶查詢角色)
    @Test
    public void test02()throws Exception{
        // 獲取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findByIdWithRoles(41);

        System.out.println(user);
    }

作業(yè):以角色為中心,查詢多個用戶

4.4 知識小結(jié)

一對一配置:使用<resultMap>+<association>做配置
    association:
        property:關(guān)聯(lián)的實體屬性名
        javaType:關(guān)聯(lián)的實體類型(別名)

一對多配置:使用<resultMap>+<collection>做配置
    collection:
        property:關(guān)聯(lián)的集合屬性名
        ofType:關(guān)聯(lián)的集合泛型類型(別名)

多對多配置:使用<resultMap>+<collection>做配置
    collection:
        property:關(guān)聯(lián)的集合屬性名
        ofType:關(guān)聯(lián)的集合泛型類型(別名)
        
多對多的配置跟一對多很相似,難度在于SQL語句的編寫。

4.5 優(yōu)化測試

public class BaseMapperTest {

    protected SqlSession sqlSession = null;

    // 此方法在測試方法執(zhí)行之前,執(zhí)行
    @Before
    public void before() {
        // 獲取sqlSession對象
        sqlSession = MyBatisUtils.openSession();// 此方法必須線程內(nèi)獨享....
    }

    // 此方法在測試地方法執(zhí)行之后,執(zhí)行
    @After
    public void after() {
        // 關(guān)閉sqlSession
        MyBatisUtils.close(sqlSession);
    }
}
public class OrderMapperTest extends BaseMapperTest { // 繼承父類,就可以直接使用 父類的方法和成員變量了

    // 一對一關(guān)聯(lián)測試
    @Test
    public void test01() throws Exception {
        // 獲取代理對象
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

        // 根據(jù)id查詢
        Order order = orderMapper.findByIdWithUser(1);
        System.out.println(order);
    }
}

老師下午總結(jié)

回顧

1.resultMap標簽的作用以及常見的異常

resultmap標簽的作用,什么時候需要使用到resultmap標簽:

    1. 單表查詢的時候?qū)嶓w類的屬性名與表的列名不對應(yīng)的情況下。

        2. 多表查詢的時候我們也要使用。

① 實體類

package com.itheima.model;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一個用戶具有多個角色
    private List<Role> roleList;


    // 一個用戶具有多個訂單
    private List<Order> orderList;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Order> getOrderList() {
        return orderList;
    }

    public void setOrderList(List<Order> orderList) {
        this.orderList = orderList;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", orderList=" + orderList +
                '}';
    }
}

②接口

package com.itheima.dao;

import com.itheima.model.User;

import java.util.List;

public interface UserDao {

    public List<User> findAll();
}


③接口對應(yīng)的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">
<mapper namespace="com.itheima.dao.UserDao">

   <!-- resultmap的使用場景:
            1. 多表查詢的時候
            2. 實體類的屬性名與表的列名不對應(yīng)
    -->
    <resultMap id="UserMapper" type="user">
       <!-- property 實體類的屬性名
        column 表的列名-->
        <result property="username" column="name"/>
    </resultMap>

    <!--注意: 一旦使用resultmap標簽,那么指定返回值類型的時候不是使用resultType了,而是使用reusltmap-->
    <select id="findAll" resultMap="UserMapper">
        select * from user
    </select>
</mapper>

④測試類(使用該測試之前必須把MyBatisUtils工具類導入)

package com.itheima.test;

import com.itheima.dao.UserDao;
import com.itheima.model.User;
import com.itheima.utils.BaseMapperTest;
import com.itheima.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyBatisTest extends BaseMapperTest {

    @Test
    public void test01(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        //獲取接口代理對象
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> list = userDao.findAll();
        System.out.println("查詢結(jié)果:"+ list);
    }

}


2.多表查詢

  1. association 封裝單個對象的屬性
  2. collection 封裝一個集合屬性

一對一

用戶表和訂單表的關(guān)系為,一個用戶有多個訂單,==一個訂單只從屬于一個用戶==

①創(chuàng)建用戶實體類

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

②創(chuàng)建訂單實體類,并且把一個訂單只屬于某一個客戶的關(guān)系描述清楚

public class Order {

    private Integer id;

    private Integer uid;

    private Date ordertime;

    private Double money;
    
    private User user;
}

③編寫接口

package com.itheima.dao;

import com.itheima.model.Order;

public interface OrderDao {

    //根據(jù)訂單的id查找訂單
    public Order findById(int id);

}

④編寫接口對應(yīng)的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">
<mapper namespace="com.itheima.dao.OrderDao">

    <!--如果是多表查詢,那么需要使用reusltMap標簽-->
    <resultMap id="OrderMap" type="order">
        <id property="id" column="oid"/>
        <result property="ordertime" column="ordertime"/>
        <result property="money" column="money"/>
        <!--如果你的屬性是一個對象,那么我們則需要使用association標簽-->
        <association property="user" javaType="User">
            <id column="id" property="id"/>
            <result column="name" property="username"/>
            <result column="birthday" property="birthday"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        </association>
    </resultMap>


    <select id="findById" resultMap="OrderMap">
        SELECT * ,o.`id` AS oid FROM USER u INNER JOIN orders o ON o.`uid`=u.`id` WHERE o.`id`=#{id}
    </select>
</mapper>

⑤測試

   //測試一對一的關(guān)系
    @Test
    public void test02(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        //獲取接口代理對象
        OrderDao orderDao = sqlSession.getMapper(OrderDao.class);
        Order order = orderDao.findById(1);

        System.out.println("訂單信息:"+ order);
    }

今日重點

  1. resultmap使用場景以及基本使用
  2. 模糊查詢, 模糊查詢只需要練習最后一種。concat(concat,"%");
  3. 動態(tài)sql語句
  4. 多表查詢 Collection標簽 association標簽

作業(yè): 今天帥哥的代碼你們練習完畢發(fā)給組長,我會問組長或者班主任要。

時間: 明天晚上九點鐘之前.

學習反饋

反饋 回復
好像模糊查詢還有一種bind標簽(這種不講是實際開發(fā)不用嗎) bind便簽是用于綁定一個變量去使用的,目前企業(yè)里面用得不多。另外mybatis里面還有很多的標簽,這里只會講解最為常用標簽.如果同學們有興趣那么可以自己去了解一下。
講一下多對多的執(zhí)行流程 等會老師會講解的。
突然學好多標簽,還不怎么接受得來;還有多表查詢覺得復雜,又聽不懂 等會老師會講解的。
感覺學的一團糟。。。很多知識只是抄的順手。。不理解的地方很多 當你抄代碼之前一定要問清楚即將要抄的語句,我們抄代碼之前一定要明白該語句的作用。 否則今晚抄完,還沒有睡著就已經(jīng)忘記了。 代碼:先看得懂,然后再去抄,才能自己寫。
看不懂的情況可以問同學,問老師。
今天內(nèi)容還是比較多,麻煩老師再講一下多表查詢這里的java代碼實現(xiàn),還不是很清楚 等會老師會講解的。

答疑區(qū)

疑問 回復
SqlSession放成員變量位置,有何線程安全問題? 說的是需要自己手動關(guān)閉。不釋放資源的話會造成內(nèi)存泄露。 后面是用spring整合之后就不用自己釋放資源了。
SqlSession放成員變量位置,有何線程安全問題? 1,SqlSession作為一個接口,其并沒有線程安全性的問題,我們常說的線程安全問題是SqlSession的一個實現(xiàn)DefaultSqlSession,
2.并發(fā)操作使用了同一個DefaultSqlSession的實例,而同一個DefaultSqlSession的實例使用的是同一個Executor對象,
為什么需要用別名,然后還特地給別名弄映射呢? 考慮你的類的字段和表字段可能不一致的場景。
那我要是換一個別名,映射是不是也得跟著改,這樣豈不是很麻煩,增加了很大工作量,還不如不寫別名 是的.我們講的是字段名稱和類中的屬性名稱不一致的解決方案.實際開發(fā)盡量避免這種不一致.就不用搞這種映射關(guān)系了.
‘%${value}%’ 括號中為什么只能寫value mybatis源碼寫死的.因此只能寫value
‘%${value}% 這種是字符串拼接 會產(chǎn)生 sql注入問題concat()也是字符串拼接呀,為什么不會有sql注入問題 ${value}是拼接sql
#{key} 這個是預編譯sql
嵌套怎么理解 concat(concat('%',#{name}),'%') 首先里面的concat使用%與name進行拼接,得到結(jié)果%#{name},然后外層再使用concat讓%#{name}與%再進行拼接
返回主鍵的意義在哪 剛剛于帥老師說了一個應(yīng)用場景,比如:我們添加了一個部門之后,需要馬上給該部門添加員工
嗯嗯我的意思就是剛 創(chuàng)建就直接獲取的意義 是吧? 假如我們使用的是ajax, 添加完一個部門的時候,馬上彈出一個框配置該部門的員工,那么這個表單是不是就要拿到剛剛的部門主鍵了。
老師,多個單引號拼接是指MySQL會將多個連續(xù)的單引號包裹的多個字符串自動拼接成一個大的字符串嗎? 是的,就是一個字符串拼接。
臨時變量item的值必須和#{}中的值一致是嗎? 是的
foreach標簽的item的屬性的值是固定的嗎 item的屬性值是可以改變的
foreach標簽對于數(shù)組類型的,怎么前面的傳入的類型(就是parameterType)又不需要帶[]了,不是int[]而是int了.這也是別名嗎 不是別名,這里寫int相當于是寫了容器中存放的類型.
為什么不寫uid屬性 直播間屏幕一直在更新.你突然問一句:為什么不寫uid屬性?
到底哪里為什么不寫?請結(jié)合上下文,完整描述自己的問題.謝謝.
JavaType屬性的是怎么寫 就是當前屬性對象的類型, 比如Order類中包含User,那么該user屬性的javaType類型則是User
before這個倆注解,能用private修飾嗎 不可以
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容