一、正规企业里的开发模式
在我们在上一章中,使用了 SqlSession封装的一些方法完成了crud操作,但是SqlSession封装的方法,传递占位符的参数只能传递一个。而且他的方法名称都是固定。
真实在开发环境下我们不使用SqlSession封装的方法,而是习惯自己定义方法,自己调用自己的方法。也就是通过dao和映射文件的关联来完成操作
1.1 实现过程
(1)创建一个dao接口(interface)并自定义需要的方法
public interface UserDao {
/**
* 查询所有
* @return
*/
public List<User> findAll();
}
(2)创建映射文件 (和上一章创建映射文件相同,如果你最后也添加了模板,我们直接new映射文件的模板即可
<?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:命名空间
必须和dao相同
-->
<mapper namespace="com.ykq.dao.UserDao">
<select id="findAll" resultType="com.ykq.entity.User">
select * from tb_user
</select>
</mapper>
这里面要特别注意的是 我们的命名空间(namespace)不能再像上一章随便的起名了 这里必须和dao的接口相同 mapper里面的sql的标签里的id也不能随便起名了 必须和你dao里面的方法相同
@Test
public void findAll()throws Exception{
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session= factory.openSession();
StudentDao studentDao = session.getMapper(StudentDao.class);
List<Student> list = studentDao.findAll();
System.out.println(list);
session.commit();
session.close();
}
这里面需要注意的是 前几行的代码和我们上一章的写法一样 但是我们不再使用session里面有的方法了 我们需要用我们自己定义的方法 那么想要实现自己定义的方法 就需要获取接口相应的代理对象
我们输出一下这个userDao变量到控制台 他会显示如下的内容:
翻译一下就知道 这个UserDao变量就相当于我们的xxx接口的代理对象了 我们直接用这个变量就可以。
1.2典型的bug
(1)namespaec和接口名不对应 xml文件没有注册到mybatis配置文件中
(2).映射文件中的标签id和接口中方法名不对应
1.3 安装mybatis插件
在我们这种规范来书写代码时,会感到有些略微繁琐和不方便,这是idea中有一个插件,可以帮助我们生成mapper xml文件,快速从代码跳转到mapper及从mapper返回代码、mybatis自动补全及语法错误提示、集成mybatis generator gui 界面、根据数据库注解、生成swagger model注解等功能,非常的方便
如果mapper中没有对应的方法,可以alt+enter自动生成(sql代码还是需要自己写的)
如果mapper里面的id没有找到与dao里面的方法名相对应的也会报红提示
二、@Param实现传递多个参数
我们在dao接口中某些方法可能需要传递多个参数,譬如: 登录(username,password),但是我们只能传一个参数。之前我们采取的办法是把多个参数封装到一个对象里再把对象当作参数传输。现在我们可以在参数处使用@Param()为参数起名。 不然不可以在 #{ } 里面写自己的参数名,会变成param1,param2..
三、返回递增的主键值(添加方法时)
需要返回添加数据库后的id值。 需要用到标签里面的两个属性:(1)useGeneratedKeys:设置使用生成的主键 (2)keyProperty:将接到的值 赋值给你实体类中的哪个属性 注意这里的属性名不能写错,不然就接不到值了。
<!--添加用户
useGeneratedKeys:设置使用生成的主键
keyProperty: 赋值给哪个属性
-->
<insert id="addUser" parameterType="com.ykq.entity.User"
useGeneratedKeys="true" keyProperty="userId">
insert into tb_user values(null,#{username},#{realname})
</insert>
测试一下:
@Test
public void testInsert() throws Exception{
SqlSession session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis.xml")).openSession();
StudentDao studentDao = session.getMapper(StudentDao.class);
Student student = new Student();
student.setName("曹操");
student.setAge(100);
System.out.println("生成前:"+student);
studentDao.add(student);
System.out.println("生成后:"+student);
session.commit();
session.close();
控制台生成的效果如下,我们可以看到,数据库里的主键id已经成功的传到了我们的id属性中。
四、解决列名和属性名不一致问题的两种方法
如果我们的实体类中的属性名,和我们的数据库里表的列名不相同,那么我们在输出查询的结果时,就接不到值,因为名称不同,不能将值传到与之对应的属性内。
这时我们就需要想办法去解决这个问题。下面提供两个方法来解决这个问题:
4.1 方法一:为查询的列起别名,而别名和属性名一致
为sql语句中查询的列名起别名。在sql语句中,我们时可以为我们查询列名起别名的
select id as '学号' from student ;
这里的as是可以省略的。我们需要通过别名将列名变为我们的实体类中对应的属性名。这样属性名和列名就可以对应起来
<!--根据id查询学生信息-->
<select id="findOne" resultType="com.ykq.entity.Student">
select stu_id id,stu_name name,stu_age age from tb_stu where stu_id=#{id}
</select>
4.2 方法二:使用resultMap完成列和属性之间的映射关系。
通过resultMap标签将列名和属性名对应起来。
需要注意的是:
sql标签中的resultMap属性和resultType二者只能用一个。
resultMap标签可以有多个。
sql标签中的resultMap的名称必须和想要使用的resultMap标签的id名相同。
如果列名和属性名有些一致的,可以在resultMap中不写映射关系
<resultMap id="StuMapper" type="com.ykq.entity.Student">
<!--主键的映射关系 column:列名 property:属性名-->
<id column="stu_id" property="id"/>
<!--普通列的映射关系-->
<result column="stu_name" property="name"/>
<result column="stu_age" property="age"/>
</resultMap>
<!--resultType和ResultMap二者只能用一个-->
<select id="findOne" resultMap="StuMapper">
select * from tb_stu where stu_id=#{id}
</select>
五、动态sql
5.1 什么是动态sql
根据参数的值,判断sql的条件。 就好比,前端传入的name值如果为空,就查询所有,如果有值,就加上条件 where name=#{name}
5.2 为什么要使用动态sql
当我们的列名特别多,数据量特别大的时候,我们不可能一条一条写sql语句。这不现实。就好比你在网上商城买东西,搜索物品时的检索条件,可以是价格范围,品牌,大小,颜色,系列等等,那么组合出来的sql语句就成千上万条了。这时就需要用到我们的动态sql。
5.3 mybatis中的动态sql标签
大致有这些,我们下面一个一个讲解
5.3.1 if标签--单条件判断
好比我们的if判断语句,如果满足条件就执行if标签内的sql语句,如果不满足就不执行。
//如果name不为null则按照name查询 如果为null则查询所有
public List<Account> findByCondition(@Param("name")String name,@Param("money") Double money);
注意:”where“是不能放在下面的if里面的。而且where后面需要跟一个恒等式。这样不管是哪种拼接方式都是合理的。这个大家可以自己悟一悟,多想一想就可以明白了。
<select id="findByCondition" resultType="com.ykq.entity.Account">
select * from account where 1=1
<if test="name!=null and name!=''">
and name=#{name}
</if>
<if test="money!=null">
and money=#{money}
</if>
</select>
5.3.2 choose标签--多条件分支判断
好比我们的switch case语句。他是自带break功能的。如果里面的<choose>相当于swtich,<when>相当于case: xxx break 。<otherwise> 相当于default。如果when里面的条件都不满足,就执行default里面的内容。
<select id="findByCondition02" resultType="com.ykq.entity.Account">
select * from account where 1=1
<choose>
<when test="name!=null and name!=''">
and name=#{name}
</when>
<when test="money!=null">
and money=#{money}
</when>
<otherwise>
and isdeleted=0
</otherwise>
</choose>
</select>
5.3.3 where标签
我们观察到上面的sql都加了 where 1=1 ,如果不使用where 1=1 那么你的动态sql可能会出错。 我们能不能不加where 1=1呢! 可以 那么我们就可以使用where标签,作用:可以自动为你添加where关键字,并且可以帮你去除第一个and |or
<select id="findByCondition" resultType="com.ykq.entity.Account">
select * from account
<where>
<if test="name!=null and name!=''">
and name=#{name}
</if>
<if test="money!=null">
and money=#{money}
</if>
</where>
</select>