(二)Mybatis增删查改、动态SQL语句、多表操作(简单易懂,图文教学,整套实战)

实战项目完成代码:MybatisTest_GitHub


上一章:(一)初见MyBatis,快速入门以及配置文件映射文件讲解


1.优化代码

我们先对昨天的代码进行一下小小的优化,在测试类中写的测试方法,如果我们每次再写一个测试方法,都需要构建工厂什么的,就会有很多重复性代码
修改测试方法MybatisTest

public class MybatisTest {

    private InputStream in;
    private SqlSession session;
    private UserDao userDao;
    //在测试方法执行之前执行
    @Before
    public void before() throws IOException {
        //1.获取工厂图纸(配置信息)
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.招募工人(创建构建者)
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3.给工人图纸建造工厂(构建者构建工厂对象)
        SqlSessionFactory factory = builder.build(in);
        //4.构建产品流水线(打开自动提交事务)
        session = factory.openSession(true);
        //5.根据产品设计图生产出产品(动态代理)
        userDao = session.getMapper(UserDao.class);
    }
    //在测试方法执行之后执行
    @After
    public void after() throws IOException {
        //7.回收(释放资源)
        session.close();
        in.close();
    }

    @Test
    public void findAll() throws IOException {
        //6.使用产品
        List<User> users = userDao.findAll();
        for (User user :
                users) {
            System.out.println(user);
        }
    }
}

修改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>
    <!--加载外部资源-->
    <properties resource="druid.properties"/>
    <!--取别名-->
    <typeAliases>
        <!--单个设置-->
        <!--<typeAlias  alias="user" type="com.lois.domain.User"/>-->
        <!--批量设置,扫描整个包下的类,别名为类名(首字母大写或小写都可以)-->
        <package name="com.lois.domain"/>
    </typeAliases>
    <environments default="mysql">
        <!--配置mysql环境-->
        <environment id="mysql">
            <!--配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源(连接池)-->
            <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="com.lois.dao"/>
    </mappers>

</configuration>

再次运行方法,查看是否成功。

2.增删查改

  1. 修改UserDao接口
public interface UserDao {
    /**
     * 查询所有用户
     * @return
     */
    public List<User> findAll();

    /**
     * 根据id查询用户
     * @param id
     * @return
     */
    public User findById(int id);

    /**
     * 保存用户
     * @param user
     */
    public void save(User user);

    /**
     * 修改用户
     * @param user
     */
    public void update(User user);

    /**
     * 根据id删除用户
     * @param id
     */
    public void deleteById(int id);

}
  1. 修改Dao映射文件UserDao.xml
<mapper namespace="com.lois.dao.UserDao">
    <select id="findAll" resultType="user" >
        select * from user;
    </select>

    <select id="findById" parameterType="int" resultType="user">
        select * from user where id = #{uid}
    </select>

    <insert id="save" parameterType="user">
        insert into user(name,age) values(#{name},#{age})
    </insert>

    <update id="update" parameterType="user">
        update user set name = #{name},age = #{age} where id = #{id}
    </update>

    <delete id="deleteById" parameterType="int" >
        delete from user where id = #{uid}
    </delete>
</mapper>
  1. 向测试类MybatisTest中添加一下方法进行测试
@Test
    public void findById(){
        //6.使用产品
        User user = userDao.findById(1);
        System.out.println(user);
    }

    @Test
    public void save(){
        User user = new User();
        user.setName("小章");
        user.setAge(33);
        userDao.save(user);
        //查看数据库表数据变化
    }

    @Test
    public void update(){
        //修改id为3的用户年龄
        User user = userDao.findById(3);
        System.out.println(user.getAge());
        user.setAge(100);
        userDao.update(user);
        //查看数据库表中id为3的用户年龄和控制台中打印的数字
    }

    @Test
    public void deleteById(){
        //删除id为3的用户
        userDao.deleteById(3);
    }
  1. 对各个测试方法进行测试,并观察数据库表中数据变化

:事实上除了select不需要事务提交,其他操作都需要事务提交,如果你factory.openSession(true)中没有设置true,也就是说没有打开自动提交事务的话,虽然你也能看到上面的测试方法执行成功,但是却没有实际效果。如果你没有打开自动提交事务,那么就需要你在每个方法后面进行手动提交事务session.commit()

3.模糊查询

下面我们来讲一下模糊查询,顺便看看在Dao映射文件中#{}和${}的区别

  1. 向UserDao接口中添加两个方法,一个准备使用#{},一个准备${}
/**
     * 模糊查询#{}
     * @param name
     * @return
     */
    public List<User> findLikeName1(String name);

    /**
     * 模糊查询${}
     * @param name
     * @return
     */
    public List<User> findLikeName2(String name);
  1. 向UserDao.xml映射文件中添加如下映射
	<select id="findLikeName1" parameterType="string" resultType="user">
        select * from user where name like #{name}
    </select>

    <select id="findLikeName2" parameterType="string" resultType="user">
        select * from user where name like '%${value}%'
    </select>
  1. 向测试类添加两个测试方法
	@Test
    public void findLikeName1(){
        System.out.println(userDao.findLikeName1("%小%"));
    }
    @Test
    public void findLikeName2(){
        System.out.println(userDao.findLikeName2("小"));
    }

不知道你发现没有,使用#{}需要在测试方法中拼接%,而使用${}不需要,这是因为#{}不能在 ’ ’ 中使用。

  1. 测试结果比较
    findLikeName1
    在这里插入图片描述
    findLikeName2
    在这里插入图片描述

结论:我们可以通过输出的sql语句知道,使用#{}是预加载的方法,而${}仅仅只是字符串拼接,所以使用#{}更好,能够防止sql注入。

4.动态SQL语句

在上面我们写的都是一些很简单的sql语句,但是在实际中,我们还会语句很复杂的sql语句,例如动态变化的SQL语句,那么下面我们讲解mybatis如何实现动态sql语句。

4.1 <if>标签

  • test属性(必填):判断条件,其中不能使用&&、||,用 and 、or 替代
  1. 向UserDao接口中添加方法
    /**
     * 动态sql——if
     * @param user
     * @return
     */
    public List<User> findByUser1(User user);
  1. 想UserDao.xml映射文件中添加映射
	<select id="findByUser1" parameterType="user" resultType="user">
        select * from user where 1=1
        <if test="name != null and name != '' ">
            and name = #{name}
        </if>
        <if test="age != null and age != 0 ">
            and age = #{age}
        </if>
    </select>

:where 1=1是为了能够拼接字符串,为了使下面的if都不成立时,sql语句还是正确

  1. 向数据库表中添加一条数据,修改save测试方法,并运行
@Test
    public void save(){
        User user = new User();
        user.setName("小明");
        user.setAge(32);
        userDao.save(user);
        //查看数据库表数据变化
    }
  1. 向测试类中添加测试方法,运行测试方法
@Test
    public void findByUser(){
        User user = new User();
        user.setName("小明");
        //user.setAge(22);
        System.out.println(userDao.findByUser1(user));
    }
  1. 放开注释,再次运行
  2. 对比结果
    在这里插入图片描述
    在这里插入图片描述
    发现拼接的字符串不同

4.2 <where>标签

<where>标签可以将需不需要加where交给交给mybatis判断,这样就不用写where 1=1 了。

  1. 修改刚刚在映射文件中的映射
<select id="findByUser1" parameterType="user" resultType="user">
        select * from user
        <where>
            <if test="name != null and name != '' ">
                and name = #{name}
            </if>
            <if test="age != null and age != 0 ">
                and age = #{age}
            </if>
        </where>
    </select>
  1. 修改测试类中测试方法,运行测试方法
@Test
    public void findByUser(){
        User user = new User();
        //user.setName("小明");
        //user.setAge(22);
        System.out.println(userDao.findByUser1(user));
    }
  1. 依次放开注释,再次运行测试方法
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4.3 <foreach>标签

当我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。这样我们将如何进行参数的传递?这时候就要用到<foreach>标签
标签属性介绍

  • collection:所要遍历的集合
  • open:拼接字符串的开头
  • close:拼接字符串的结尾
  • item:集合中取出的单个数据别名
  • separator:每个item之间使用什么符合隔开
  • <foreach>标签的内容,即为在open和close之间的遍历数据
  1. 创建一个QueryVo类用于存储List集合参数
public class QueryVo implements Serializable {
    private List<Integer> ages;

    public List<Integer> getAges() {
        return ages;
    }

    public void setAges(List<Integer> ages) {
        this.ages = ages;
    }
}
  1. 向UserDao接口中添加方法
public List<User> findByUserInAge(QueryVo queryVo);
  1. 向UserDao.xml映射添加映射
	<select id="findByUserInAge" resultType="user" parameterType="QueryVo">
        select * from user
        <where>
            <if test="ages != null and ages.size() > 0">
                <foreach collection="ages" open="age in ( " close=")" item="age" separator=",">
                    #{age}
                </foreach>
            </if>
        </where>
    </select>
  1. 向测试类中添加测试方法,运行测试方法
@Test
    public void findByUserInAge(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(22);
        list.add(32);
        QueryVo queryVo = new QueryVo();
        queryVo.setAges(list);
        System.out.println(userDao.findByUserInAge(queryVo));
    }
  1. 分析运行结果
    在这里插入图片描述
    可以看到遍历了两个数据,添加在sql语句后面

5 多表查询

5.1 准备工作

  1. 创建子工程day2
  2. 将day1中资源都复制过来
  3. 将UserDao.xml、UserDao接口类和QueryVo实体类删除
  4. 删除数据库表user中的第三条记录(使user表中只有两条数据,id为1和2)
  5. 执行sql脚本,创建表account
use mybatistest;
drop table if exists `account`;
create table `account`(
	`id` int(10) primary key auto_increment,
	`uid` int(10),
	`money` double
);
insert into account(uid,money) values (1,2000),(2,3000),(1,3200);

drop table if exists `role`;
create table `role`(
	`id` int(10) primary key auto_increment,
	`name` varchar(30),
	`desc` varchar(60)
);
insert into `role`(`name`,`desc`) values ('董事长','公司的最高领导者'),('总经理','公司管理者'),('财务部长','管理公司财务');

drop table if exists `user_role`;
create table `user_role`(
	`uid` int(10),
	`rid` int(10)
);
insert into `user_role` values (1,1),(1,2),(2,2),(2,3);

5.2 一对一(多对一)

5.2.1 方式一
  1. 创建com.lois.domain.Account实体类,生成get、set、tostring方法
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
}
  1. 编写sql语句
select account.*,user.name,user.age from user,account where user.id = account.uid;

在这里插入图片描述

  1. 根据上图,我们需要编写com.lois.domain.AccountUser实体类,:该类继承了Account类
public class AccountUser extends Account implements Serializable {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return super.toString()+"AccountUser{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 编写com.lois.dao.AccountDao接口类
public interface AccountDao {
    /**
     * 查询所有账户,同时获取账户所有人及年龄
     * @return
     */
    public List<AccountUser> findAll();
}
  1. 编写com/lois/dao/AccountDao.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="com.lois.dao.AccountDao">
    <select id="findAll" resultType="accountuser">
        select account.*,user.name,user.age from user,account where user.id = account.uid;
    </select>
</mapper>
  1. 修改测试类com.lois.test.MybatisTest
public class MybatisTest {

    private InputStream in;
    private SqlSession session;
    private AccountDao accountDao;
    //在测试方法执行之前执行
    @Before
    public void before() throws IOException {
        //1.获取工厂图纸(配置信息)
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.招募工人(创建构建者)
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3.给工人图纸建造工厂(构建者构建工厂对象)
        SqlSessionFactory factory = builder.build(in);
        //4.构建产品流水线(打开自动提交事务)
        session = factory.openSession(true);
        //5.根据产品设计图生产出产品(动态代理)
        accountDao = session.getMapper(AccountDao.class);
    }
    //在测试方法执行之后执行
    @After
    public void after() throws IOException {
        //7.回收(释放资源)
        session.close();
        in.close();
    }

    @Test
    public void findAllAccount(){
        System.out.println(accountDao.findAll());
    }
}
  1. 运行结果(3条记录)
[Account{id=1, uid=1, money=2000.0}AccountUser{name='小明', age=22},
 Account{id=2, uid=2, money=3000.0}AccountUser{name='小红', age=25},
  Account{id=3, uid=1, money=3200.0}AccountUser{name='小明', age=22}]
5.2.2 方式二
  1. 修改com.lois.domain.Account实体类,生成get、set、tostring方法
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;

    private User user;
}
  1. 修改AccountDao接口findAll方法的返回值List<Account>
public List<Account> findAll();
  1. 修改AccountDao.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="com.lois.dao.AccountDao">
    <resultMap id="accountMap" type="account">
        <id column="id" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
        <!--映射单个对象-->
        <association property="user" javaType="user">
            <result column="name" property="name"/>
            <result column="age" property="age"/>
        </association>
    </resultMap>

    <select id="findAll" resultMap="accountMap">
        select account.*,user.name,user.age from user,account where user.id = account.uid;
    </select>
</mapper>
  1. 运行测试
[Account{id=1, uid=1, money=2000.0, user=User{id=null, name='小明', age=22, accounts=null}}, 
Account{id=2, uid=2, money=3000.0, user=User{id=null, name='小红', age=25, accounts=null}}, 
Account{id=3, uid=1, money=3200.0, user=User{id=null, name='小明', age=22, accounts=null}}]

5.3 一对多

  1. 编写sql语句
select user.*,account.* from user left join account on user.id = account.uid;

在这里插入图片描述
可以看到表中有两个id,这是因为在user和account中都有id,所以我们要对sql语句进行修改(取别名)

select user.*,account.id as aid,account.uid,account.money from user left join account on user.id = account.uid;

在这里插入图片描述

  1. 修改User实体类,向实体类中添加List<Account> accounts ,并重新生成set和get、tostring方法
public class User implements Serializable {
    private Integer id;
    private String name;
    private Integer age;

    private List<Account> accounts;
}
  1. 编写用户持久层com.lois.dao.UserDao接口
public interface UserDao {
    /**
     * 查询所有用户,并且查询出用户下所有账户信息
     * @return
     */
    public List<User> findAll();
}
  1. 编写com/lois/dao/UserDao.xml映射类,需要使用resultMap,因为我们查出来的字段跟实体类不匹配
<?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.lois.dao.UserDao">
    <resultMap id="userMap" type="user">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <!--collection 是用于建立一对多中集合属性的对应关系
            ofType 用于指定集合元素的数据类型-->
        <collection property="accounts" ofType="account">
            <id column="aid" property="id"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userMap">
        select user.*,account.id as aid,account.uid,account.money from user left join account on user.id = account.uid;
    </select>
</mapper>
  1. 向测试类com.lois.test.MybatisTest添加方法,userDao在测试类中自行获取
@Test
    public void findAllUser(){
        System.out.println(userDao.findAll());
    }
  1. 运行测试
[User{id=1, name='小明', age=22, accounts=[Account{id=1, uid=1, money=2000.0}, Account{id=3, uid=1, money=3200.0}]}, 
User{id=2, name='小红', age=25, accounts=[Account{id=2, uid=2, money=3000.0}]}]

可以看见查出了两个用户,用户1名下有2个账户

5.4 对多对

前面我们学习了一对多,多对多其实就可以看成双向的一对多关系。一个用户可以有多个角色,一个角色可以有多个用户

  1. 编写sql语句
select role.*,user.name as uname,user.age from role left join user_role on (role.id = user_role.rid) left join user on (user_role.uid = user.id);

在这里插入图片描述

  1. 编写实体类com.lois.domain.Role ,生成set,get,tostring方法
public class Role implements Serializable {
    private Integer id;
    private String name;
    private String desc;

    private List<User> users;
}
  1. 编写com.lois.dao.RoleDao接口类
public interface RoleDao {
    /**
     * 查询所有角色,以及拥有角色的用户
     * @return
     */
    public List<Role> findAll();
}
  1. 编写com/lois/dao/RoleDao.xml映射文件,因为我们在sql语句中使用了别名,所以需要使用resultMap
<?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.lois.dao.RoleDao">
    <resultMap id="roleMap" type="role">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="desc" property="desc"/>
        <collection property="users" ofType="user">
            <result column="uname" property="name"/>
            <result column="age" property="age"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="roleMap">
        select role.*,user.name as uname,user.age from role left join user_role on (role.id = user_role.rid) left join user on (user_role.uid = user.id);
    </select>
</mapper>
  1. 向测试类com.lois.test.MybatisTest添加方法,roleDao在测试类中自行获取
@Test
    public void findAllRole(){
        System.out.println(roleDao.findAll());
    }
  1. 运行测试
[Role{id=1, name='董事长', desc='公司的最高领导者', users=[User{id=null, name='小明', age=22, accounts=null}]}, 
Role{id=2, name='总经理', desc='公司管理者', users=[User{id=null, name='小明', age=22, accounts=null}, User{id=null, name='小红', age=25, accounts=null}]},
 Role{id=3, name='财务部长', desc='管理公司财务', users=[User{id=null, name='小红', age=25, accounts=null}]}]

可以看到查询出来了三个角色,在角色总经理上有两个用户(当然这可能不符合常理,但是我们也是测试)

6.作业

刚刚我们说了多对多是两个一对多,我们上面已经完成了一个一对多,那么另一个“一用户对多角色”就需要你自己补充了!


上一章:(一)初见MyBatis,快速入门以及配置文件映射文件讲解(构建思路讲解,图文教学,整套实战)

下一章:(三)Mybatis延迟加载(懒加载)、一(二)级缓存、注解开发(简单易懂,图文教学,整套实战)


相关内容:

(一)初见MyBatis,快速入门以及配置文件映射文件讲解(构建思路讲解,图文教学,整套实战)

(二)Mybatis增删查改、动态SQL语句、多表操作(简单易懂,图文教学,整套实战)

(三)Mybatis延迟加载(懒加载)、一(二)级缓存、注解开发(简单易懂,图文教学,整套实战)

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis是一种Java持久层框架,它可以通过映射文件来实现对数据库的增删查改操作。 1. 增加操作: 在Mybatis中,可以使用insert语句来进行数据的插入操作。需要在映射文件中定义对应的SQL语句,然后通过SqlSession对象的insert()方法来执行操作。 例如: ``` <insert id="addUser" parameterType="com.example.User"> insert into user(name, age, sex) values(#{name}, #{age}, #{sex}) </insert> ``` 在Java代码中调用: ``` User user = new User(); user.setName("张三"); user.setAge(20); user.setSex("男"); SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("addUser", user); sqlSession.commit(); ``` 2. 删除操作: 在Mybatis中,可以使用delete语句来进行数据的删除操作。需要在映射文件中定义对应的SQL语句,然后通过SqlSession对象的delete()方法来执行操作。 例如: ``` <delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete> ``` 在Java代码中调用: ``` int id = 1; SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.delete("deleteUser", id); sqlSession.commit(); ``` 3. 查询操作: 在Mybatis中,可以使用select语句来进行数据的查询操作。需要在映射文件中定义对应的SQL语句,然后通过SqlSession对象的selectOne()方法来执行操作。 例如: ``` <select id="getUserById" parameterType="int" resultType="com.example.User"> select * from user where id = #{id} </select> ``` 在Java代码中调用: ``` int id = 1; SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("getUserById", id); ``` 4. 修改操作: 在Mybatis中,可以使用update语句来进行数据的修改操作。需要在映射文件中定义对应的SQL语句,然后通过SqlSession对象的update()方法来执行操作。 例如: ``` <update id="updateUser" parameterType="com.example.User"> update user set name = #{name}, age = #{age}, sex = #{sex} where id = #{id} </update> ``` 在Java代码中调用: ``` User user = new User(); user.setId(1); user.setName("李四"); user.setAge(25); user.setSex("女"); SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.update("updateUser", user); sqlSession.commit(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值