mybatis第二天

该文档是:mybatis框架学习...

博客连接:https://www.loveuluo.cn

日期:2021-01-02

1. 搭建环境

创建maven工程:

image-20210102171547516

数据库:

image-20210103093508168

User类:

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    //自动生成的get set方法
}

测试类中将公共的部分抽取:

public class MyBatisTest {
    private SqlSession sqlSession;
    private InputStream in=null;

    //用于返回一个UserDao的代理对象
    public UserDao chuShi() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象
        sqlSession = factory.openSession();
        //4.使用SqlSession创建Dao接口的代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        return userDao;
    }
    //用于关闭连接的方法
    public void destroy() throws IOException {
        sqlSession.close();
        in.close();
    }

    //测试查询操作
    @Test
    public void Test01() throws IOException {
        //直接调用方法得到代理对象
        UserDao userDao = chuShi();
        List<User> all = userDao.findAll();
        System.out.println(all);
        //调用方法关闭连接
        destroy();
    }
}

2. 基于代理Dao实现CRUD操作

2.1 保存操作

2.1.1 正常保存

(1)在UserDao接口增加saveUser方法

/**
 * 保存用户
 * @param user
 * @return
 */
Integer saveUser(User user);

(2)在用户的映射配置文件中配置

细节:
resultType属性: 用于指定结果集的类型。
parameterType属性:用于指定传入参数的类型。
sql语句中使用#{}字符:它代表占位符,相当于原来jdbc部分所学的?,都是用于执行语句时替换实际的数据。具体的数据是由#{}里面的内容决定的。
#{}:里边写的其实就是,例如你的parameterType是一个User实体类,那么里边就要写User实体类的属性,例如username对应的就是User类中的getUsername()方法也就是username属性。

<!--保存用户-使用insert标签-->
<!--parameterType:告诉mybatis参数的类型,values里边的值是从传的参数里边取,
例如User实体类,values可以直接写User属性的名称(其实就是get方法后边的名称首字母小写)。-->
<insert id="saveUser" parameterType="com.Luo.domain.User">
    INSERT INTO user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})
</insert>

(3)测试类

//测试保存方法
@Test
public void Test02() throws IOException {
    UserDao userDao = chuShi();
    //创建user对象
    User user=new User();
    user.setUsername("张三");
    user.setAddress("台州");
    user.setBirthday(new Date());
    user.setSex("男");
    //通过代理对象保存user
    Integer integer = userDao.saveUser(user);
    //提交事务
    sqlSession.commit();
    System.out.println("成功插入"+integer+"条数据");
    destroy();
}

2.1.2 问题扩展:新增用户id的返回值

(1)在用户的映射配置文件中配置

注意一:新增用户后,同时还要返回当前新增用户的id值,因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长auto_increment的值返回。

注意二:selectKey中的语句解释,使用LAST_INSERT_ID() 这个函数也是获取最后插入的记录的id,这个函数需要和AUTO_INCREMENT 属性一起使用,当往带有AUTO_INCREMENT属性字段的表中新增记录时,LAST_INSERT_ID()即返回该字段的值。

<!--保存用户-使用insert标签-->
<insert id="saveUser" parameterType="com.Luo.domain.User">
<!--配置插入操作后,获取插入数据的id-->
<!--!keyProperty:实体类的id
    keyColumn:数据库的id
    resultType:返回的类型
    order:什么时候执行获取id的操作,这里是after代表插入之后-->
    <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>

(2)测试类

//测试保存方法并返回自增id
@Test
public void Test02() throws IOException {
    UserDao userDao = chuShi();
    User user=new User();
    user.setUsername("张三");
    user.setAddress("台州");
    user.setBirthday(new Date());
    user.setSex("男");

    //执行保存操作之前打印一下user
    System.out.println("保存操作之前"+user);
    //通过代理对象保存user
    Integer integer = userDao.saveUser(user);
    //提交事务
    sqlSession.commit();
    //执行保存操作之后打印一下user
    System.out.println("保存操作之后"+user);
    System.out.println("成功插入"+integer+"条数据");
    destroy();
}

(3)结果:

image-20210102201606518

2.2 修改操作

(1)在UserDao接口增加updateUser方法

/**
 * 更新用户
 * @param user
 */
void updateUser(User user);

(2)在用户的映射配置文件中配置

<!--更新用户-->
<update id="updateUser" parameterType="com.Luo.domain.User">
    UPDATE user SET username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} WHERE id=#{id}
</update>

(3)测试类

//测试更新用户
@Test
public void Test03() throws IOException {
    UserDao userDao = chuShi();
    //创建user对象
    User user=new User();
    user.setId(50);
    user.setUsername("张三");
    user.setAddress("台州");
    user.setBirthday(new Date());
    user.setSex("女");
    //通过代理对象保存user
    userDao.updateUser(user);
    //提交事务
    sqlSession.commit();
    destroy();
}

2.3 删除操作

(1)在UserDao接口增加deleteUser方法

/**
 * 删除用户
 * @param integer
 */
void deleteUser(Integer integer);

(2)在用户的映射配置文件中配置

<!--删除用户-->
<!--parameterType的类型为Integer,根据id删除用户-->
<!--当参数的值只有一个的时候#{}里的内容可以随便写-->
<delete id="deleteUser" parameterType="Integer">
    DELETE FROM user WHERE id=#{uid}
</delete>

(3)测试类

//测试删除用户
@Test
public void Test04() throws IOException {
    UserDao userDao = chuShi();
    //通过代理对象删除user
    userDao.deleteUser(50);
    //提交事务
    sqlSession.commit();
    destroy();
}

2.4 查询一条数据

(1)在UserDao接口增加findById方法

/**
 * 查询一条数据
 * @param integer
 */
User findById(Integer integer);

(2)在用户的映射配置文件中配置

<!--查询一条数据-->
<select id="findById" parameterType="int" resultType="com.Luo.domain.User">
    SELECT * FROM user WHERE id=#{uid}
</select>

(3)测试类

//测试查询一条数据
@Test
public void Test05() throws IOException {
    UserDao userDao = chuShi();
    //通过代理对象查询一条数据
    User user = userDao.findById(48);
    System.out.println(user);
    //提交事务
    sqlSession.commit();
    destroy();
}

2.5 模糊查询

2.5.1 第一种配置方式

(1)在UserDao接口增加findByName方法

/**
 * 根据名字模糊查询
 * @param name
 * @return
 */
List<User> findByName(String name);

(2)在用户的映射配置文件中配置

<!--根据名字模糊查询-->
<select id="findByName" parameterType="String" resultType="com.Luo.domain.User">
    SELECT * FROM user WHERE username like #{name}
</select>

(3)测试类

//测试根据名字模糊查询
@Test
public void Test06() throws IOException {
    UserDao userDao = chuShi();
    //通过代理对象模糊查询
    List<User> userList = userDao.findByName("%王%");
    System.out.println(userList);
    //提交事务
    sqlSession.commit();
    destroy();
}

2.5.2 第二种配置方式

(1)配置文件的sql语句修改,${里边只能写value}

<!--根据名字模糊查询-->
<select id="findByName" parameterType="String" resultType="com.Luo.domain.User">
    SELECT * FROM user WHERE username like '%${value}$%'
</select>

(2)测试类中不需要传百分百

//测试根据名字模糊查询
@Test
public void Test06() throws IOException {
    UserDao userDao = chuShi();
    //通过代理对象模糊查询
    List<User> userList = userDao.findByName("王");
    System.out.println(userList);
    //提交事务
    sqlSession.commit();
    destroy();
}

(3)看看源码为什么${里边只能写value}

image-20210102193827737

2.5.3 两种的区别

开发中使用第二种较多!

image-20210102193713460

2.6 查询使用聚合函数

(1)在UserDao接口增加findTotal方法

/**
 * 查询使用聚合函数
 * @return
 */
Integer findTotal();

(2)在用户的映射配置文件中配置

<!--查询使用聚合函数-->
<select id="findTotal" resultType="Integer">
    SELECT COUNT(*) FROM user
</select>

(3)测试类

//测试查询使用聚合函数
@Test
public void Test07() throws IOException {
    UserDao userDao = chuShi();
    //通过代理对象查询聚合函数
    Integer count = userDao.findTotal();
    System.out.println(count);
    //提交事务
    sqlSession.commit();
    destroy();
}

2.7 Mybatis与JDBC编程的比较

image-20210102192810147

3. Mybatis参数的深入

3.1 传递pojo包装对象

作用是:如果查询的条件要从多个对象里取出来,就先把这多个对象包装成一个,例如多表联查。

image-20210103091455124

(1):OGNL表达式:

image-20210103091246883

(2)编写QueryVo实体类

//查询条件对象
public class QueryVo {
    //User对象作为参数
    private User user;
    public User getUser() {return user;}
    public void setUser(User user)  this.user = user;}
}

(3)在UserDao接口增加findUserByVo方法

/**
 * 使用QueryVo作为查询条件
 * @param vo
 * @return
 */
List<User> findUserByVo(QueryVo vo);

(4)在用户的映射配置文件中配置

<!--使用QueryVo作为查询条件-->
<select id="findUserByVo" parameterType="com.Luo.domain.QueryVo" resultType="com.Luo.domain.User">
    <!--这里代表的是用QueryVo对象中的user属性中的username属性进行模糊查询-->
    SELECT * FROM user WHERE username like #{user.username}
</select>

(5)测试类

//测试根据QueryVo作为条件进行模糊查询
@Test
public void Test08() throws IOException {
    UserDao userDao = chuShi();
    User user=new User();
    user.setUsername("%王%");
    //把user对象设置进QueryVo对象中
    QueryVo vo=new QueryVo();
    vo.setUser(user);
    //根据QueryVo作为条件进行查询
    List<User> users = userDao.findUserByVo(vo);
    System.out.println(users);
    //提交事务
    sqlSession.commit();
    destroy();
}

3.2 实体类属性和数据库列名不一致

3.2.1 类似于这种情况

image-20210103094125799

(1)编写LikeUser实体类:

public class LikeUser {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    //get set方法
}

(2)在UserDao接口增加findUserByVo方法

/**
 * 根据LikeUser对象查到LikeUser对象,演示用
 * @param user
 * @return
 */
LikeUser findByLikeUser(LikeUser user);

(3)在用户的映射配置文件中配置

<!--根据LikeUser对象查到LikeUser对象,演示用-->
<!--查询参数的类型是LikeUser实体类,返回值也是LikeUser实体类(根据名字性别和住址查询)-->
<select id="findByLikeUser" parameterType="com.Luo.domain.LikeUser" resultType="com.Luo.domain.LikeUser">
    SELECT * FROM user WHERE username=#{userName} AND sex=#{userSex} AND address=#{userAddress}
</select>

(4)测试类

//测试根据LikeUser的名字性别和住址查询并返回LikeUser对象
@Test
public void Test09() throws IOException {
    UserDao userDao = chuShi();
    //创建user对象
    LikeUser likeUser=new LikeUser();
    likeUser.setUserName("小马宝莉");
    likeUser.setUserAddress("北京修正");
    likeUser.setUserSex("女");
    //根据LikeUser的名字性别和住址查询并返回LikeUser对象
    LikeUser returnLikeUser = userDao.findByLikeUser(likeUser);
    //输出一下返回的LikeUser对象看看有多少数据封装进入
    System.out.println(returnLikeUser);
    //提交事务
    sqlSession.commit();
    destroy();
}

(5)输出的结果

只有username被封装进来了,因为实体类的属性是userName而数据库列名是username,mysql不区分大小写所以成功封装。而别的属性数据的列名都对应不上实体类属性,所以封装失败。

image-20210103095401183

3.2.2 解决方法1 - 起别名

这种效率是最高的,因为是在mysql的层面。

(1)修改配置文件

这样的话,数据库查询出来的结果的数据的列名就和实体类的属性名一致了

<select id="findByLikeUser" parameterType="com.Luo.domain.LikeUser" resultType="com.Luo.domain.LikeUser">
    SELECT id as userId,username as userName,birthday as userBirthday,sex as userSex,address as userAddress
    FROM user WHERE username=#{userName} AND sex=#{userSex} AND address=#{userAddress}
</select>

(2)输出的结果

image-20210103100323032

3.2.3 解决方法2 - 配置的方式

mybatis提供的配置方法,比较方便只需要配置一次,可以直接拿来使用。

(1)修改配置文件

<!--配置 查询结果的列名和实体类的熟悉名的对应关系-->
<resultMap id="LikeUser->User" type="com.Luo.domain.LikeUser">
    <!--主键字段的对应-->
    <!--property代表的是实体类的属性 column代表的是数据库的列名-->
    <id property="userId" column="id"></id>
    <!--非主键字段的对应-->
    <result property="userName" column="username"></result>
    <result property="userBirthday" column="birthday"></result>
    <result property="userSex" column="sex"></result>
    <result property="userAddress" column="address"></result>
</resultMap>

<!--根据LikeUser对象查到LikeUser对象,演示用-->
<!--resultType改为resultMap代表使用上边配置的-->
<select id="findByLikeUser" parameterType="com.Luo.domain.LikeUser" resultMap="LikeUser->User">
    SELECT * FROM user WHERE username=#{userName} AND sex=#{userSex} AND address=#{userAddress}
</select>

(2)输出的结果

image-20210103102256569

4. 使用实现UserDao接口的方式CRUD(了解)

image-20210103123217999

(1)编写UserDao接口(了解内容,仅用两个功能来举例)

public interface UserDao {
    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     * @return
     */
    Integer saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
}

(2)UserDao接口实现类

public class UserDaoimpl implements UserDao {

    private SqlSessionFactory factory;
    //传入SqlSessionFactory对象根据传入的才能造出SqlSession对象
    public UserDaoimpl(SqlSessionFactory factory) {
        this.factory = factory;
    }

    @Override
    public List<User> findAll() {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SqlSession中的方法,实现查询列表
        //  第一个参数:用哪个语句来执行
        List<User> users = session.selectList("com.Luo.dao.UserDao.findAll");
        //3.释放资源
        session.close();
        return users;
    }

    @Override
    public Integer saveUser(User user) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SqlSession中的方法,实现查询列表
        //  第一个参数:用哪个语句来执行,第二个参数:需要的参数
        int i = session.insert("com.Luo.dao.UserDao.saveUser",user);
        //3.提交事务
        session.commit();
        //4.释放资源
        session.close();
        return i;
    }
}

(3)在用户的映射配置文件中配置

<?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.Luo.dao.UserDao"><!--对应接口所在位置-->
    
    <!--配置查询所有-->
    <select id="findAll" resultType="com.Luo.domain.User">
        SELECT * FROM user
    </select>

    <!--保存用户-使用insert标签-->
    <insert id="saveUser" parameterType="com.Luo.domain.User">
        <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>
    
</mapper>

(4)测试类

public class MyBatisTest {
    private InputStream in=null;
    private UserDao userDao=null;

    //用于给userDao属性赋上UserDaoimpl实体类
    public void chuShi() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂对象创建dao对象
        userDao = new UserDaoimpl(factory);
    }
    //用于关闭连接的方法
    public void destroy() throws IOException {
        in.close();
    }

    //1.测试查询操作
    @Test
    public void Test01() throws IOException {
        //调用此方法给userDao属性赋值UserDaoImpl
        chuShi();
        //通过UserDaoImpl查询所有
        List<User> all = userDao.findAll();
        System.out.println(all);
        //调用方法关闭连接
        destroy();
    }
    //2.测试保存方法
    @Test
    public void Test02() throws IOException {
        //调用此方法给userDao属性赋值UserDaoImpl
        chuShi();
        //创建user对象
        User user=new User();
        user.setUsername("张三");
        user.setAddress("台州");
        user.setBirthday(new Date());
        user.setSex("男");

        //通过UserDaoImpl保存user
        Integer integer = userDao.saveUser(user);
        System.out.println("成功插入"+integer+"条数据");
        destroy();
    }
}

5. Mybatis中使用Dao实现类的执行分析过程(了解)

(1)查询的执行过程:

image-20210103154153000

(2)增删改的执行过程:

image-20210103154432613

6. Mybatis中使用代理Dao的分析过程(了解)

image-20210103151841865

7. SqlMapConfig.xml配置文件

7.1 properties(属性)

(1)增加jdbcConfig.properties配置文件
image-20210103195949558

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456

(2)修改SqlMapConfig.xml配置文件

此时我们的dataSource标签就变成了引用上面的配置(properties标签中已经引入外部配置文件jdbcConfig.properties)

<?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">
<!--mybatis主配置文件-->
<configuration>
    
    <!--配置properties
        可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息
        resource属性:常用的
            用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
    -->
    <properties resource="jdbcConfig.properties"><!--这是引用外部配置文件的写法-->
        <!--这是不引用外部文件直接写的方法,下边的dataSource中也可以取到,不过这样做没啥意义
        <property name="driver" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>-->
    </properties>

    <!-- 配置mybatis的环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境 -->
        <environment id="mysql">
            <!-- 配置事务的类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
            <dataSource type="POOLED">
                <!--用${}取到上边properties标签中的外部配置-->
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>

    <!-- 告知mybatis映射配置的位置 -->
    <mappers>
        <!--改为class并且指定到UserDao接口-->
        <mapper class="com.Luo.dao.UserDao"/>
    </mappers>
</configuration>

7.2 typeAliases(类型别名)

(1)修改SqlMapConfig.xml配置文件

<configuration>
<!--使用typeAliases配置别名,它只能配置domain中类的别名-->
<typeAliases>
    <!--typeAliases用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就不再区分大小写-->
    <!--这是配置单个实体类的别名-->
    <!--<typeAlias type="com.Luo.domain.User" alias="user"></typeAlias>-->

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

(2)上边配置好了之后,UserDao.xml就能使用别名代替全限定类名

不区分大小写

image-20210103201830310

7.3 mappers(映射器)

(1)<mapper resource=" " />

使用相对于类路径的资源
如:<mapper resource="com/itheima/dao/UserDao.xml" />

(2)<mapper class=" " />

使用mapper接口类路径
如:<mapper class="com.itheima.dao.UserDao"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

(3)<package name=""/>

image-20210103202510214

最后修改:2021 年 01 月 21 日 04 : 36 PM
如果觉得我的文章对你有用,请随意赞赏