Mybatis之mapper接口开发
(一)需求,为什么要使用接口开发?
- 在测试用例中,在调用session的方法的时候,都会传入要调用的SQL的namespace+id名称,这不是必须的。可以只传入id即可。但是,如果在mybatis的环境中有多个相同id的映射名称,就会报错。所以,一般情况下,调用方法最好还是使用namespace+id。
- 但是,namespace+id的使用方式很容易报错,因为是string类型的,没有检查。所以,mybatis提供了一种非常好的设计方式来避免这种问题,即Mapper接口。
接口开发的规范:namespace的值=接口的包名+接口的类名
注意:
1、 包名 + 类名 = XXXMapper.xml中namespace的值
2、 接口中方法名 = mapper.xml中 具体的SQL语句定义的id值
3、 方法的返回值和参数要和映射文件中一致(当数据库的字段名和对象的属性名一致时,可以用简单属性resultType。但是当数据库中的字段名称和对象中的属性名称不一致时,就需要resultMap 属性。)
/* <select id="all" resultType="User"> select * from user </select> */ public List<User> all(); //这是接口中的方法
(二)Mybatis怎么做的?
@Test public void testMapper(){ SqlSession session = MyBatisUtil.openSession(); try{ UserMapper mapper = session.getMapper(UserMapper.class); System.out.println(mapper.getClass().getName()); }finally{ session.close(); } }
打印结果:
$Proxy4
很简单了,mybatis为接口做了一个动态代理。在执行UserMapper接口上面的方法时,参考接口的全路径名,即可找到对应的UserMapper.xml,在执行接口上面的每一个方法的时候,实际上 就是在执行namespace+id,mybatis在根据定义的方法的元素,选择调用合适的session的方法来执行,并传入参数就可以。
使用Mapper接口的方式,在集成Spring+MyBatis也非常方便。因为我们可以直接把Mapper接口看作domain的dao接口了。
(三)接口开发的三个特点
1、 Mapper接口方法名和mapper.xml中定义sql的id值相同
2、 Mapper接口方法接收的参数类型和mapper.xml中定义的sql 的parameterType的类型相同
3、 Mapper接口方法的返回值类型和mapper.xml中定义的sql的resultType的类型相同
(四)自动匹配规范驼峰规则
数据库中我们习惯使用全大写,多个单词用下划线隔开,而po对象,习惯使用java驼峰规则。那一个一个手工写resultMap字段,浪费开发时间。Mybatis可以配置mapUnderscoreToCamelCase,实现自动映射。这个值默认为true。
1 sqlMapConfig.xml中配置settings
<?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> <settings> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings> </configuration>
-
在XXXmapper文件中 resultMap配置autoMapping="true"
<resultMap type="cn.tedu.jk.domain.Contract" id="contractRM" autoMapping="true"> <id property="id" column="CONTRACT_ID"/> </resultMap>
注意:主键需要单独写,其它字段就可以直接利用驼峰规则自动映射。
(五)SQL模糊查询
1 参数中直接加入%%(不好)
如果要发起一条SQL,where name like #{name},name手动传入%大%这里就容易发生SQL注入问题
2 Sql中用CONCAT函数 (推荐) 三种数据库的字符串拼接
Sql Server 中没有Concat函数 拼接 直接用 + 号
Oracle 和 MySQL 可以用Concat进行拼接
<!-- where 2--> <select id="findone2" resultType="User" parameterType="map"> select <include refid="cols"></include>
from user <where> id=#{id} and addr=#{addr} and name like concat(concat('%',#{name}),'%') </where> </select>
(六)【重点】关联关系
1 准备数据
1.3.1.1 用户表user_info create table user_info( id int primary key auto_increment, user_name varchar(100), user_addr varchar(200), user_age int); set names gbk; insert into user_info values(null,’韩梅梅’,’上海’,20); insert into user_info values(null,’王海涛’,’北京’,30); insert into user_info values(null,’张慎政’,’河南’,10); 1.3.1.2 用户扩展表user_extra create table user_extra( id int primary key auto_increment, user_id int, work varchar(100), salary double); insert into user_extra values(null,’1’,’程序员’,100000); insert into user_extra values(null, ’2’,’教师’,1000); insert into user_extra values(null, ’3’,’CTO’,100000); 1.3.1.3 订单表orders create table orders( id int primary key auto_increment, user_id int, order_no int, order_desc varchar(100), price double); insert into orders values(null,1,100,’好评’,1000); insert into orders values(null,2,200,’优秀’,100); insert into orders values(null,1,300,’优秀’,100); insert into orders values(null,1,400,’优秀’,100);
2 封装对象
public class UserInfo { //id映射 private int id; //user_name映射 private String userName; //user_addr映射 private String userAddr; //user_age映射 private int userAge;
自己生成一下get,set方法,和toString 方法 (Ecilipse快捷键 SHIFT+ALT+S)
}
public class UserExtra { //id映射 private int id; //work映射 private String work; //salary映射 private double salary; //user_id映射(对应user表的id) private int userId;
自己生成一下get,set方法,和toString 方法 (Ecilipse快捷键 SHIFT+ALT+S)
}
public class Orders { //id映射 private int id; //user_id映射 private int userId; //order_no映射 private int orderNo; //order_desc映射 private String orderDesc; //price映射 private double price;
自己生成一下get,set方法,和toString 方法 (Ecilipse快捷键 SHIFT+ALT+S)
}
3 根据用户查询一对一关系的用户扩展信息,使用association + javaType
3.1改造UserInfo对象 (将UserExtra对象引入)
public class UserInfo { private int id; private String userName; private String userAddr; private int userAge; //一对一 private UserExtra userExtra;
自己生成一下get,set方法,和toString 方法 (Ecilipse快捷键 SHIFT+ALT+S)
}
3.2编写UserInfoMapper.xml(引入核心配置文件中去!!association + javaType)
<?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="cn.tedu.dao.UserInfoDao"> <!-- 一对一的UserExtra信息 type="" 把数据封装给哪个实体对象(全路径) id="userExtraRM" 唯一标志 --> <resultMap type="cn.tedu.pojo.UserInfo" id="userExtraRM"> <!-- association用来描述一对一关系 property="userExtra" 在UserInfo中的属性 javaType="" 关联对象的类的全路径... --> <association property="userExtra" javaType="cn.tedu.pojo.UserExtra"> <!-- 描述UserExtra信息... --> <id column="id" property="id" /> <result column="work" property="work" /> <result column="salary" property="salary" /> <result column="user_id" property="userId" /> </association> </resultMap> <!-- 根据用户id查询用户详情 --> <!-- 一对一的关联查询 resultMap="" 引用上面大的结果集,id值 --> <select id="getExtraByUserId" resultMap="userExtraRM"> select * from user_info t1, user_extra t2 where t1.id=t2.user_id and t1.id=#{id} </select> </mapper>
3.3 创建UserInfoDAO接口
public interface UserInfoDao { //根据用户id查询用户的信息,一对一 public UserInfo getExtraByUserId(int userId); }
3.4测试代码
public class ORMTest { // 1,读取配置文件,创建ssf对象 SqlSessionFactory ssf; @Before public void init(){ try { InputStream inputStream = Resources.getResourceAsStream( "sqlMapConfig.xml"); ssf = new SqlSessionFactoryBuilder() .build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //测试对象的一对一关系 @Test public void One2One(){ //创建sqlsession,执行SQL SqlSession session = ssf.openSession(); //接口开发 UserInfoDao dao = session.getMapper(UserInfoDao.class); //调用接口方法 UserInfo info = dao.getExtraByUserId(1); System.out.println(info); session.close(); } }
4 一对多 查询用户的所有订单,使用collection + ofType
4.1改造UserInfo对象 (将UserExtra对象引入)
public class UserInfo { private int id; private String userName; private String userAddr; private int userAge; private UserExtra userExtra; //一对多 private List<Orders> orders;
自己生成一下get,set方法,和toString 方法 (Ecilipse快捷键 SHIFT+ALT+S)
}
4.2 改造UserInfoMapper.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="cn.tedu.dao.UserInfoDao"> <!-- 一对多的关联关系 type="" 结果集封装到哪个对象身上 extends="userInfoRM" 和其他resultMap的关联关系 --> <resultMap type="cn.tedu.pojo.UserInfo" id="ordersRM" extends="userInfoRM"> <!-- 多的一方Orders的对象信息 property="" UserInfo对象中的属性名 ofType="" 关联对象Orders的全路径 --> <collection property="orders" ofType="cn.tedu.pojo.Orders"> <!-- 描述Orders的信息 --> <id column="id" property="id" /> <!-- 普通字段 --> <result column="user_id" property="userId" /> <result column="order_no" property="orderNo" /> <result column="order_desc" property="orderDesc" /> <result column="price" property="price" /> </collection> </resultMap> </mapper>
增加关联查询的SQL 注意:结果集中,不要出现同名的字段,否则封装不成功!!如果出现重复字段,以结果集为准,重新映射column关系。 <!-- 根据用户id查询订单信息 resultMap="ordersRM"引用其他resultMap,id值 --> <select id="getOrdersByUserId" resultMap="ordersRM"> select * from user_info t1, orders t2 where t1.id=t2.user_id and t1.id=#{id} </select>
4.3改造UserInfoDao接口
public interface UserInfoDao { //根据用户id查询用户的信息,一对一 public UserInfo getExtraByUserId(int userId); //据用户id查询订单信息 public List<UserInfo> getOrdersByUserId(int userId); }
4.4改造测试类
@Test public void One2Many(){ //创建sqlsession,执行SQL SqlSession session = ssf.openSession(); //接口开发 UserInfoDao dao = session.getMapper(UserInfoDao.class); //调用接口方法,根据用户id查询订单 List<UserInfo> list = dao.getOrdersByUserId(1); for (UserInfo userInfo : list) { System.out.println(userInfo); } session.close(); }
相信一万小时天才理论
最穷不过要饭,不死终会出头!