昨天半天时间肝完基于注解的Mybatis,发现的确太散乱了,但真就简便!详见前篇:Mybatis框架(1)–注解
今天来搞一搞写起来比较繁琐但是一目了然的基于xml配置的Mybatis。其实大差不差,原理都是那么一回事~
以下为个人跟着视频内容整理,有错误请一定指出!勿喷,谢谢!视频来源:B站:BV1mE411X7yp
黑马程序出品不会错!!!强推~(黑马快打钱 x)
走起!
先上代码,再基于代码解释。架构同前篇,蓝色框框中的文件不再赘述,直接看前篇~
User.java (User属性和数据库user表中的列名命名不统一 ->
xml配置需要根据每一个持久层Dao接口在resources下创建一个对应的xml文件!
IUserDao.xml 配置如下:
<mapper namespace="com.demo.dao.IUserDao">
<resultMap id="userMap" type="user">
<id property="userId" column="id">id>
<result property="userName" column="username">result>
<result property="userAddress" column="address">result>
<result property="userSex" column="sex">result>
<result property="userBirthday" column="birthday">result>
resultMap>
<select id="findAll" resultMap="userMap">
select * from user;
select>
<insert id="saveUser" parameterType="user">
<selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
selectKey>
insert into user(username,address,sex,birthday)values(#{userName},#{userAddress},#{userSex},#{userBirthday});
insert>
<update id="updateUser" parameterType="USER">
update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id=#{userId}
update>
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{uid}
delete>
<select id="findById" parameterType="INT" resultMap="userMap">
select * from user where id = #{uid}
select>
<select id="findByName" parameterType="string" resultMap="userMap">
select * from user where username like #{name}
select>
<select id="findTotal" resultType="int">
select count(id) from user;
select>
<select id="findUserByVo" parameterType="com.demo.domain.QueryVo" resultMap="userMap">
select * from user where username like #{user.userName}
select>
mapper>
parameterType:输入参数的类型
分为3种情况:
OGNL(Object Graphic Navigation Language):通过对象的取值方法来获取数据,省略get就能获取。
e.g.
java类中的写法 user.getUserName();
OGNL写法:user.userName
而在mybatis的配置文件中无需写前面的”对象."是因为:在parameterType已经指定了属性所属的类。
这里来解释之前提到的“最后一个方法”
domain包中除了实体类User外,又新建了一个名为QueryVo的实体类,把其他实体类对象作为了它的属性,实现pojo套pojo【禁止套娃 】。
在IUDao.xml配置文件中,这里where后的查询条件可以用QueryVo类下的对象的属性来限制,达到多表查询的作用。
<select id="findUserByVo" parameterType="com.demo.domain.QueryVo" resultMap="userMap">
select * from user where username like #{user.userName}
select>
在测试类中:
@Test
public void testFindByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUserName("%花%");
vo.setUser(user);
List<User> users = userDao.findUserByVo(vo);
for(User u:users){
System.out.println(u);
}
}
最后再补充一下dao对应xml的总体结构:
<mapper namespace="对应dao层接口的全限定类名">
resultMap标签:当实体类属性和数据库表中不一致时需要在此产生映射
id属性:这组映射的名称
type属性:是什么类型的
id标签:主键属性
result标签:非主键的属性
property属性:实体类属性名
colunm属性:数据库表中的列名
【注意】
如果在主配置文件中没起别名,在接口配置文件中 resultMap下的type以及parameterType指向pojo(包装)对象时必须要用全限定类名!不然会报如下错:
但处处都要全限定写起来不方便,这也体现出在主配置文件中起别名的重要性:
<typeAliases>
<package name="com.demo.domain">package>
typeAliases>
需求1:查询user,但根据什么参数查询未知。可以是username,也可是sex,其他所有属性都可能。
【难道要有几个属性写几条select吗? NO!】
解决:用where if标签
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user
<where>
<if test="userName != null">
and username = #{userName}
if>
<if test="userSex != null">
and sex = #{userSex}
if>
where>
select>
【注意】
1. if标签内只能用‘and’ 不可用‘&&’!
2. if标签之间要有‘and’ 勿忘!
需求2:如何实现如下sql?如何传参(括号中有几个未知)?
select * from user where id = (1,2,3,4,5,.....);
解决:利用之前提到过的pojo包装对象->queryvo
<sql id="defaultUser">
select * from user
sql>
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
<include refid="defaultUser">include>
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="id in (" close=")" item="uid" separator=",">
#{uid}
foreach>
if>
where>
select>
这里引入:foreach标签
collection属性:取为集合名
open属性:起始于。“id in”是接着select * from user where
后面写的
close属性:终止于 。 【跳出循环后得到的集合元素放在open和close之间】
item属性:foreach过程中每次遍历到的元素名【foreach标签内的#{}名字同item!!!】
separator属性:此处表示用逗号隔开每个元素【用于格式限制】
这里还巧用了include标签,指向了id名为“defaultUser”的那一段,在共有段代码很长时有利于减少重复。妙!
对于Mybatis而言,一个账户只属于一个用户(但一个用户可以有多个账户),属于一对一范畴(站在一个account的立场上)。
需求:查询每个account及其所属用户的信息。
<mapper namespace="com.itheima.dao.IAccountDao">
<resultMap id="accountUserMap" type="account">
<id property="id" column="id">id>
<result property="uid" column="uid">result>
<result property="money" column="money">result>
<association property="user" column="uid" javaType="user">
<id property="id" column="id">id>
<result column="username" property="username">result>
<result column="address" property="address">result>
<result column="sex" property="sex">result>
<result column="birthday" property="birthday">result>
association>
resultMap>
<select id="findAllAccount" resultMap="accountUserMap">
select a.*,u.username,u.address from account a , user u where u.id = a.uid;
select>
mapper>
其中在resultMap标签下用association标签补充外键的内容。
association标签里,
property属性:和account其他属性一样,user现在就是account属性之一;
colunm属性:user在account表中是以uid代表的;
javatype:是什么实体类;
在association标签下填写user的每个属性,此处照旧。
最后记得在select查询标签中补全属性resultMap,勿忘!
package com.itheima.test;
import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class AccountTest {
private InputStream in;
private SqlSession sqlSession;
private IAccountDao accountDao;
@Before//用于在测试方法执行之前执行
public void init()throws Exception{
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
accountDao = sqlSession.getMapper(IAccountDao.class);
}
@After//用于在测试方法执行之后执行
public void destroy()throws Exception{
//提交事务
// sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有账户,同时包含用户名称和地址
*/
@Test
public void testFindAllAccountUser(){
List<Account> aus = accountDao.findAllAccount();
for(Account au : aus){
System.out.println(au);
System.out.println(au.getUser());
}
}
}
与刚刚一对一的立场相反,就是多对一:对于user而言,一个user可以有好几个account。
需求:查询每个user开的account
【会存在有的user没开account的情况】
<mapper namespace="com.itheima.dao.IUserDao">
<resultMap id="userAccountMap" type="user">
<id property="id" column="id">id>
<result property="username" column="username">result>
<result property="address" column="address">result>
<result property="sex" column="sex">result>
<result property="birthday" column="birthday">result>
<collection property="accounts" ofType="account">
<id column="aid" property="id">id>
<result column="uid" property="uid">result>
<result column="money" property="money">result>
collection>
resultMap>
<select id="findAll" resultMap="userAccountMap">
select * from user u left outer join account a on u.id = a.uid
select>
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
select>
mapper>
其中collection标签表示用集合存储user对应的accounts(可能会有多个)【这里注意!!!里面的property属性的值一定和实体类中表示另一个表的属性名一致!!!!!否则报错!!详见文章:Mybatis遇到的报错集合】,ofType代表集合中元素的类型,其他的照旧对应好每个属性名和列名。
就是两边都是多,在xml配置文件中用collection标签。举一反三很简单啦!