【应用篇】MyBatis学习笔记

MyBatis学习笔记


一 环境配置

1 什么是MyBatis?

​ MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。

​ 特点:使用MyBatis框架时,不需要编写实现类(原生的JDBC代码),只需要专注需要执行的sql命令。

2 相关jar包

MyBatis依赖包

3 MyBatis全局配置文件 MyBatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!-- dtd文档,包含命名空间-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--全局配置标签-->
<configuration>
    <!-- 设置LOG4J日志包生效-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <typeAliases>
        <!--为整个包起别名,所有包下的类名为resultType中属性值-->
        <package name="cn.thetian.pojo"/>
    </typeAliases>
    <!--环境标签-->
    <environments default="default">
        <!--声明可以使用的环境mysql,oracle等-->
        <environment id="default">
            <!--使用原生JDBC事务-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/ajax?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射标签,扫描mapper下的xml文件和mapper接口-->
    <mappers>
        <package name="cn.thetian.mapper"/>
    </mappers>
</configuration>

4 LOG4J日志配置文件

文件名:log4j.properties(必须用这个名字且需要处在src根目录下,用来调试程序以及记录日志)

log4j.rootCategory=INFO, CONSOLE, LOGFILE
#将mapper包设为DEBUG级别来查看sql语句
log4j.logger.cn.thetian.mapper=DEBUG

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%C %d{YYYY.MM.dd hh:mm:ss} %m %n

log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.file=D://my.log
#true 表示追加 false表示不追加
log4j.appender.LOGFILE.Append = false
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.Conversionattern=%m %n

二 一个简单的用例

1 设计数据库

就以一个简单的用户表为例子

在这里插入图片描述

2 编写pojo(例:User)

public class User {
    private int id;
    private String name;
    private String sex;
    private int age;
    private String hobby;
    
    //GetterAndSetter();
    //toString();
}

3 编写pojoMapper.xml(例: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">
<!-- namespace 为mapper起名,通过namespace.id调用不同的标签-->
<mapper namespace="cn.thetian.mapper.UserMapper">
    <!-- 
	id:标签id,一般以方法名命名 
	resultType:返回值类型,未起别名时要用包名加类名表示返回类型,自动使用AutoMapping特性,即一条记录对应一个对象,数据库中字段名要与类的属性名要保持一致。
	-->
    <select id="selAll" resultType="User">
        select * from user
    </select>
</mapper>

4 测试

/**
 * 测试session中的selectList
 * 1 获取mybatis.xml全局配置文件的字节流
 * 2 利用工厂模式和构建者模式获取工厂
 * 3 生产SqlSession
 * 4 利用SqlSession运行指定mapper的xml文件中的namespace+方法名
 * 5 返回List
 */
public class Test01 {
    public static void main(String[] args) throws IOException {
        //获取MyBatis.xml文件的字节流
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        //工厂设计模式
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        //生产SqlSession
        SqlSession session = factory.openSession();

        List<User> userList = session.selectList("cn.thetian.mapper.UserMapper.selAll");
        for (User user:userList){
            System.out.println(user.toString());
        }
        //关闭session
        session.close();
    }
}

result:

==> Preparing: select * from user
> Parameters:
<
Total: 5
User{id=1, name=‘张三’, sex=‘男’, age=18, hobby=‘打游戏,看动漫’}
User{id=2, name=‘李四’, sex=‘男’, age=20, hobby=‘打张三,写代码’}
User{id=29, name=‘六六’, sex=‘女’, age=22, hobby=‘主播’}
User{id=30, name=‘张飞’, sex=‘男’, age=8000, hobby=‘俺也一样’}
User{id=31, name=‘王五’, sex=‘男’, age=666, hobby=‘helloworld’}

三 增删查改

1 增(insert标签)

<insert id="insUser" parameterType="User">
    <!-- #{key} 可以获取到参数值(value)(底层是将user转为map)-->
    insert into user values (default, #{name}, #{sex}, #{age}, #{hobby});
</insert>

demo:

try {
	User user = new User();
	user.setName("六六");
	user.setSex("女");
	user.setAge(22);
	user.setHobby("主播");
    
    //将一个user对象作为参数传递到userMapper.xml文件中
	int result = session.insert("cn.thetian.mapper.UserMapper.insUser", user);
	System.out.println(result>0?"添加成功":"添加失败");
	}catch (Exception e){
    	//出现异常回滚事务
		session.rollback();
	}
//增删改查需要手动提交事务(可以再MyBatis.xml文件中设置自动提交	)
session.commit();
session.close();

2 删(delete标签)

<delete id="delUser" parameterType="user">
	delete from user where id=#{id}
</delete>

demo:

try {
	User user = new User();
	user.setId(31);
	int delete = session.delete("cn.thetian.mapper.UserMapper.delUser", user);
	System.out.println(delete>0?"删除成功":"删除失败");
}catch (Exception e){
    //如果出现异常,回滚事务
	session.rollback();
}
session.commit();
session.close();

3 查

3.1 selectOne

**特点:**查询一个对象或变量,返回值为一个基本数据类型或者一个引用数据类型对象

<!-- 查询user表的行数-->
<select id="selCount" resultType="int">
    select count(*) from user
</select>

demo:

int num = session.selectOne("cn.thetian.mapper.UserMapper.selCount");
System.out.println(num);
session.close();

result:

==> Preparing: select count(*) from user
> Parameters:
<
Total: 1
5

3.2 selectList

特点: 查询多条记录,返回集合,举例 标题二

3.3 selectMap

特点: 将数据库中某一字段当作map的key(取pojo中的字段) 返回->以参数为key,以返回结果为值的map

<select id="selMap" resultType="User">
	select * from user
</select>
Map<Object, Object> map = session.selectMap("cn.thetian.mapper.UserMapper.selMap","name");
System.out.println(map);
session.close();

result:

==> Preparing: select * from user
> Parameters:
<
Total: 5
{李四=User{id=2, name=‘李四’, sex=‘男’, age=20, hobby=‘打张三,写代码’}, …

4 改(update)

<update id="updAge">
	update user set age=age+1
</update>

demo:

try {
	int index = session.update("cn.thetian.mapper.UserMapper.updAge");
	System.out.println(index);
}catch (Exception e){
	session.rollback();
}
session.commit();
session.close();

result:

==> Preparing: update user set age=age+1
> Parameters:
<
Updates: 5
5

四 MyBatis接口绑定和注解

1 接口绑定方案

MyBatis接口绑定:创建一个接口来实现maaper.xml文件中的增删改查,外部通过调用接口来处理sql

UserMapper:

public interface UserMapper {
    List<User> selAll();
    List<User> selById(int id);
    List<User> selMap();
    int selCount();
    List<User> selByLimit(Map map);
    int insUser(User user);
    int delUser(User user);
    int updAge();
}

如果使用注解,可以不用编写UserMapper.xml配置文件

public interface UserMapper {
    @Select("select * from user")
    List<User> selAll();
    
    @Select("select * from user where id=#{param1}")
    List<User> selById(int id);
    
    @Select("select * from user")
    List<User> selMap();
    
    @Select("select count(*) from user")
    int selCount();
    
    @Select("select * from user limit #{pageNum},#{pageSize}")
    List<User> selByLimit(Map map);
    
    @Insert("insert into user values (default, #{name}, #{sex}, #{age}, #{hobby})")
    int insUser(User user);
    
    @Delete("delete from user where id=#{id}")
    int delUser(User user);
    
    @Update("update user set age=age+1")
    int updAge();
}

当使用接口绑定后可以通过一下方式调用:

InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);

在这里插入图片描述

效果等同于使用session调用不同的 namespace+id

2 多参数传递

使用场景:

当需要一次传递多个参数(不使用map)时

使用方法:

在接口中利用注解 @Param 底层原理是将 参数转化为map

@Select("select * from account where account=#{param1} and name=#{param2}")
Account selAccNoName(@Param("account") String account, @Param ("name") String name);

demo:

Account account = accountMapper.selAccNoName("1", "刘备");
System.out.println(account);

result:

==> Preparing: select * from account where account=? and name=?
> Parameters: 1(String), 刘备(String)
<
Total: 1
Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘刘备’}

五 动态SQL

由于利用注解实现动态sql很繁琐,所以牵扯到动态sql的部分任然使用配置文件

1 where用法

需求: 查询账户,查询条件:无任何条件,按账户名查找,按名字查找(可是实现任意单个条件查找或者组合条件查找)

xml:

<select id="selWhereAccNoName" resultType="Account">
	<!--基本数据类型和String 应该用0,1,param1,param2等-->
	select * from account
    <!-- where标签会自动去掉第一个and-->
	<where>
        <!-- 如果传递的账户对象中含有有效 账户名-->
		<if test="account!=null and account!=''">
			and account=#{account}
		</if>
        <!-- 如果传递的账户对象中含有有效 名字-->
		<if test="name!=null and name!=''">
			and name=#{name}
		</if>
	</where>
</select>

demo:

Scanner scanner = new Scanner(System.in);
System.out.println("请输入账户名");
String account = scanner.nextLine();
System.out.println("请输入名字");
String name = scanner.nextLine();//直接回车跳过 相当于空串而不是null
List<Account> accountList = accountMapper.selChooseAccNoName(account, name);
System.out.println(accountList);
session.close();

result:

1 查找所有账户,即不输入账户名和名字

请输入账户名

请输入名字

==> Preparing: select * from account
> Parameters:
<
Total: 3
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘刘备’},…

2 按账户名查找账户

请输入账户名
1
请输入名字

==> Preparing: select * from account WHERE account=?
> Parameters: 1(String)
<
Total: 1
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘刘备’}]

3 按名字查找用户

请输入账户名

请输入名字
张飞
==> Preparing: select * from account WHERE name=?
> Parameters: 张飞(String)
<
Total: 1
[Account{id=3, account=‘3’, password=1, balance=5100.0, name=‘张飞’}]

4 按账户名和名字查找用户

请输入账户名
1
请输入名字
刘备
==> Preparing: select * from account WHERE account=?
> Parameters: 1(String)
<
Total: 1
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘刘备’}]

2 choose when otherwise用法

xml:

<select id="selChooseAccNoName" resultType="Account">
    select * from account
	<where>
		<choose>
            <when test="account!=null and account!=''">
	            and account=#{account}
		    </when>
			<when test="name!=null and name!=''">
				and name=#{name}
	        </when>
        </choose>
	</where>
</select>

用法和使用 where+if 语句类似,可实现相同的需求

3 set用法

xml:

<!-- 使用set 去掉生成语句最后面的逗号,在最前方加set-->
<update id="updBalance" parameterType="Account">
	update account
	<set>
        <!-- 为了避免set语句为空需要写一个无关句-->
        id=#{id},
    	<if test="balance!=null">
			balance=#{balance},
    	</if>
	</set>
	where id=#{id}
</update>

demo:

Account newAccount = new Account();
newAccount.setId(1);
newAccount.setBalance(123456);
int i = accountMapper.updBalance(newAccount);
System.out.println(i>0?"修改成功":"修改失败");
session.close();

result:

==> Preparing: update account SET id=?, balance=? where id=?
> Parameters: 1(Integer), 123456.0(Double), 1(Integer)
<
Updates: 1
修改成功

4 trim用法

使用trim代替set标签和where子句

xml:

<!-- 使用trim实现where和set
    prefixOverrides 去掉生成语句最前面的"and"
    prefix 在生成语句最前加添加"set(默认后面跟个空格)
    suffix 在生成语句最后放添加"参数"
    suffixOverrides 去掉生成语句最后面的"逗号"
    执行的顺序为>先去掉内容,后添加内容
-->
<update id="pudByTrim" parameterType="Account">
	update account
    <trim prefix="set" suffixOverrides=",">
		<if test="id!=null">
        	id=#{id},
		</if>
		<if test="balance!=null">
	        balance=#{balance},
        </if>
	</trim>
    <trim prefix="where" prefixOverrides="and">
		<if test="id!=null">
	        and id=#{id}
        </if>
	</trim>
</update>

结果与只是用set类似

5 bind用法

需求: 模糊查询

xml

<select id="selDIY" parameterType="Account" resultType="Account">
	<if test="name!=null and name!=''">
        <!-- 获取name后再前后各加一个%进行字符匹配-->
        <bind name="name" value="'%'+name+'%'"/>
	</if>
    	select * from account
	<trim prefix="where name like">
	    <if test="name!=null and name!=''">
			#{name}
		</if>
	</trim>
</select>

demo:

Account newAccount = new Account();
newAccount.setName("备");
List<Account> accountList = accountMapper.selDIY(newAccount);
System.out.println(accountList);
session.close();

result:

==> Preparing: select * from account where name like ?
> Parameters: %备%(String)
<
Total: 1
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘刘备’}]

6 foreach用法

需求1:in查询

xml:

<select id="selByIn" parameterType="list" resultType="Account">
	select * from account where id in
    <!-- 使用foreach操作集合-->
	<foreach collection="list" item="var" open="(" close=")" separator=",">
		#{var}
	</foreach>
</select>

demo:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Account> accountList = accountMapper.selByIn(list);
System.out.println(accountList);
session.close();

result:

==> Preparing: select * from account where id in ( ? , ? , ? )
> Parameters: 1(Integer), 2(Integer), 3(Integer)
<
Total: 3
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘刘备’}, …

需求2:批量新增

xml:

<insert id="insBatch" parameterType="list">
	insert into account values
	<trim suffixOverrides=",">
		<foreach collection="list" item="var">
            <!--账户名密码都为var-->
	        (default ,#{var},#{var},0,'测试名'),
		</foreach>
	</trim>
</insert>

demo:

//需要SqlSession session = factory.openSession(ExecutorType.BATCH);否则会出异常ExecutorException: Executor was closed.
List<Integer> list = new ArrayList<>();
for (int i=10;i<20;i++){
	list.add(i);
}
accountMapper.insBatch(list);
session.commit();
session.close();

result:

==> Preparing: insert into account values (default ,?,?,0,‘测试名’), (default ,?,?,0,‘测试名’), …

==> Parameters: 10(Integer), 10(Integer), 11(Integer), 11(Integer), …

六 关联查询

数据库设计:

在这里插入图片描述

POJO设计(假设一个学生只有一个老师,但一个老师可以有多个学生):

在这里插入图片描述

需求: 在查询学生的同时,也要查出他们所关联的老师

1 业务装配

顾名思义,就是把两个相关联的表单独查询出来,在业务层拼装成联合查询的结果

注解:

1:StudentMapper

@Select("select * from student")
List<Student> selAll();

2:TeacherMapper

@Select("select * from teacher where id=#{0}")
Teacher selById(int id);

demo:

public class Test04 {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
        
        List<Student> studentList = studentMapper.selAll();
        for (Student student:studentList){
            student.setTeacher(teacherMapper.selById(student.getTid()));
            System.out.println(student);
        }
        session.close();
    }
}

result:

==> Preparing: select * from student
> Parameters:
<
Total: 6
==> Preparing: select * from teacher where id=?
> Parameters: 1(Integer)
<
Total: 1
Student{id=1, name=‘ZT’, age=15, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
==> Preparing: select * from teacher where id=?
> Parameters: 2(Integer)
<
Total: 1
Student{id=2, name=‘万志’, age=17, tid=2, teacher=Teacher{id=2, name=‘释迦摩尼’}}
Student{id=3, name=‘张飞’, age=7999, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
==> Preparing: select * from teacher where id=?
> Parameters: 3(Integer)
<
Total: 1
Student{id=4, name=‘刘备’, age=8001, tid=3, teacher=Teacher{id=3, name=‘王阳明’}}
Student{id=5, name=‘关羽’, age=8000, tid=2, teacher=Teacher{id=2, name=‘释迦摩尼’}}
Student{id=6, name=‘鲁智深’, age=18, tid=3, teacher=Teacher{id=3, name=‘王阳明’}}

2 resultMap标签

使用resultMap实现关联单个对象(N+1)

xml:

<resultMap id="teacherMapping" type="student">
	<id column="id" property="id"></id>
	<result column="name" property="name"></result>
	<result column="age" property="age"></result>
	<result column="tid" property="tid"></result>
	<association property="teacher" select="cn.thetian.mapper.TeacherMapper.selById" column="tid"></association>
</resultMap>
<select id="selAll" resultMap="teacherMapping">
	select * from student
</select>

demo:

public class Test04 {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> studentList = studentMapper.selAll();
        for (Student student:studentList){
            System.out.println(student);
        }
        session.close();
    }
}

result:

==> Preparing: select * from student
==> Parameters:
====> Preparing: select * from teacher where id=?
> Parameters: 1(Integer)
<
Total: 1
====> Preparing: select * from teacher where id=?
> Parameters: 2(Integer)
<
Total: 1
==> Preparing: select * from teacher where id=?
> Parameters: 3(Integer)
<
Total: 1
<
Total: 6
Student{id=1, name=‘ZT’, age=15, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=2, name=‘万志’, age=17, tid=2, teacher=Teacher{id=2, name=‘释迦摩尼’}}
Student{id=3, name=‘张飞’, age=7999, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=4, name=‘刘备’, age=8001, tid=3, teacher=Teacher{id=3, name=‘王阳明’}}
Student{id=5, name=‘关羽’, age=8000, tid=2, teacher=Teacher{id=2, name=‘释迦摩尼’}}
Student{id=6, name=‘鲁智深’, age=18, tid=3, teacher=Teacher{id=3, name=‘王阳明’}}

3 AutoMapping特性

xml:

<resultMap id="teacherMapping" type="student">
	<id column="id" property="id"></id>
	<result column="name" property="name"></result>
    <result column="age" property="age"></result>
	<result column="tid" property="tid"></result>
    <association property="teacher" javaType="teacher">
		<id column="tid" property="id"></id>
	    <result column="tname" property="name"></result>
    </association>
</resultMap>
<select id="selAll" resultMap="teacherMapping">
	select s.*,t.name tname from student s join teacher t on s.tid=t.id;
</select>

demo:

public class Test04 {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> studentList = studentMapper.selAll();
        for (Student student:studentList){
            System.out.println(student);
        }
        session.close();
    }
}

result:

==> Preparing: select s.*,t.name tname from student s join teacher t on s.tid=t.id;
> Parameters:
<
Total: 6
Student{id=1, name=‘ZT’, age=15, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=2, name=‘万志’, age=17, tid=2, teacher=Teacher{id=2, name=‘释迦摩尼’}}
Student{id=3, name=‘张飞’, age=7999, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=4, name=‘刘备’, age=8001, tid=3, teacher=Teacher{id=3, name=‘王阳明’}}
Student{id=5, name=‘关羽’, age=8000, tid=2, teacher=Teacher{id=2, name=‘释迦摩尼’}}
Student{id=6, name=‘鲁智深’, age=18, tid=3, teacher=Teacher{id=3, name=‘王阳明’}}

pper = session.getMapper(StudentMapper.class);
List studentList = studentMapper.selAll();
for (Student student:studentList){
System.out.println(student);
}
session.close();
}
}


**result:**

>==>  Preparing: select s.*,t.name tname from student s join teacher t on s.tid=t.id;  
>==> Parameters:  
><==      Total: 6 
>Student{id=1, name='ZT', age=15, tid=1, teacher=Teacher{id=1, name='孔子'}}
>Student{id=2, name='万志', age=17, tid=2, teacher=Teacher{id=2, name='释迦摩尼'}}
>Student{id=3, name='张飞', age=7999, tid=1, teacher=Teacher{id=1, name='孔子'}}
>Student{id=4, name='刘备', age=8001, tid=3, teacher=Teacher{id=3, name='王阳明'}}
>Student{id=5, name='关羽', age=8000, tid=2, teacher=Teacher{id=2, name='释迦摩尼'}}
>Student{id=6, name='鲁智深', age=18, tid=3, teacher=Teacher{id=3, name='王阳明'}}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值