Mybatis参数传递问题
(一)需要注意的地方
需要注意的是配置dao接口和dao.xml映射的时候,xml和注解不能同时存在,只能照着一个使用,不要两个都用,xml的优先级高,两个使用会报错.
传递是单个参数的时候如果传递单个参数,变量可以是简单类型:int、long、float、String、Integer、Long、Boolean、Float等。
如果传递多个参数,使用实体类封装,封装后再传给sql。有一个名词叫pojo(Plain Ordinary Java Object),里面有许多属性以及它们的getter/setter方法。
(二)pojo和数据库表字段不匹配问题
1.xml配置结果集映射
<resultMap id=”RoleMap” type=”com.itheima4.domain.Role”>
<**id column="ID" property="id"**/><br /> <**result column="ROLE_NAME" property="roleName"**/><br /> <**result column="ROLE_DESC" property="roleDesc"**/><br /> <!--这个是配置一对多了...-->
<**collection property="userList" ofType="com.itheima4.domain.User"**><br /> <**id column="id" property="id"**/><br /> <**result column="username" property="username"**/><br /> <**result column="sex" property="sex"**/><br /> <**result column="address" property="address"**/><br /> <**result column="birthday" property="birthday"**/><br /> </**collection**><br /> </**resultMap**>
2.注解配置结果集映射
/
查询所有用户
/
@Select(“select * from user”)
/
将映射关系直接配在方法上面
id是供别人引用的, value是配置映射关系的
/
@Results(id=”userMap”,value= {
//这里是主键(id=true )
//column 对应数据库字段
//property对应实体类属性
@Result(id=*true,column=”id”,property=”userId”),
//这里是普通字段
@Result(column=”username”,property=”userName”),
@Result(column=”sex”,property=”userSex”),
@Result(column=”birthday”,property=”userBirthday”),
@Result(column=”address”,property=”userAddress”),
})
List
(三)返回值规范
返回单条记录时,resultType=“pojo类型”,结果集的列名必须等于pojo的属性名。
(注意单条记录中的多个值不能分散的返回,MyBatis不支持)
返回多条记录时,resultType=“pojo类型(集合泛型)”,结果集的列名必须等于pojo的属性名。
返回单值时,resultType=“简单类型”,值直接返回给java程序。
在
在
ü
ü 综合查询的时候
查询需要多种查询维度的条件, 比如淘宝查询:需要 订单基本信息 用户信息 售后信息
就需要自定义vo查询条件的包装类
自定义查询条件的包装类可以包含简单类型,对象,数组,list等
(四)OGNL用法
(五)其它
1.byte[]类型对应关系
byte[] 一般对应数据库中的blob ,longvarbinary 以及一些和二进制流有关的字段类型
2.实体类尽量不要用基本数据类型
在java中基本类型会有默认值,比如 private int age; 字段时,创建这个类的时候,age的默认值为0 ,当使用age属性时,它总会有默认值,在某些情况下无法实现age为null,并且在动态sql的时候如果使用age!=null进行判断,结果总会为true,进而导致很多隐藏的问题,
所以在实体类中不要使用基本类型.
(六)参数概念问题
第 1 个:DAO
DAO(Data Access Object)数据访问对象,它是一个面向对象的数据库接口,负责持久层的操作,为业务层提供接口,主要用来封装对数据库的访问,常见操作无外乎 CURD。我们也可以认为一个 DAO 对应一个 POJO 的对象,它位于业务逻辑与数据库资源中间,可以结合 PO 对数据库进行相关的操作。
第 2 个:PO
PO(Persistent Object)持久层对象,它是由一组属性和属性的get和set方法组成,最简单的 PO 就是对应数据库中某个表中的一条记录(也就是说,我们可以将数据库表中的一条记录理解为一个持久层对象),多个记录可以用 PO 的集合,PO 中应该不包含任何对数据库的操作。PO 的属性是跟数据库表的字段一一对应的,此外 PO 对象需要实现序列化接口。
第 3 个:BO
从业务模型的角度看,见UML元件领域模型中的领域对象。封装业务逻辑的java对象,通过调用DAO方法,结合PO,VO进行业务操作。
BO(Business Object)业务层对象,是简单的真实世界的软件抽象,通常位于中间层。BO 的主要作用是把业务逻辑封装为一个对象,这个对象可以包括一个或多个其它的对象。举一个求职简历的例子,每份简历都包括教育经历、项目经历等,我们可以让教育经历和项目经历分别对应一个 PO,这样在我们建立对应求职简历的 BO 对象处理简历的时候,让每个 BO 都包含这些 PO 即可。
第 4 个:VO
VO也可以说封装了页面展示内容需要的数据,页面需要什么数据多张表的,都封装到VO里面
VO(Value Object)值对象,通常用于业务层之间的数据传递,和 PO 一样也是仅仅包含数据而已,但 VO 应该是抽象出的业务对象,可以和表对应,也可以不对应,这根据业务的需要。 如果锅碗瓢盆分别为对应的业务对象的话,那么整个碗柜就是一个值对象。此外,VO 也可以称为页面对象,如果称为页面对象的话,那么它所代表的将是整个页面展示层的对象,也可以由需要的业务对象进行组装而来。
第 5 个:DTO
DTO(Data Transfer Object)数据传输对象,主要用于远程调用等需要大量传输对象的地方,比如我们有一个交易订单表,含有 25 个字段,那么其对应的 PO 就有 25 个属性,但我们的页面上只需要显示 5 个字段,因此没有必要把整个 PO 对象传递给客户端,这时我们只需把仅有 5 个属性的 DTO 把结果传递给客户端即可,而且如果用这个对象来对应界面的显示对象,那此时它的身份就转为 VO。使用 DTO 的好处有两个,一是能避免传递过多的无用数据,提高数据的传输速度;二是能隐藏后端的表结构。常见的用法是:将请求的数据或属性组装成一个 RequestDTO,再将响应的数据或属性组装成一个 ResponseDTO.
第 6 个:POJO
POJO(Plain Ordinary Java Object)简单的 Java 对象,实际就是普通的 JavaBeans,是为了避免和 EJB(Enterprise JavaBean)混淆所创造的简称。POJO 实质上可以理解为简单的实体类,其中有一些属性及其getter和setter方法的类,没有业务逻辑,也不允许有业务方法,也不能携带有connection之类的方法。POJO 是 JavaEE 世界里面最灵活的对象,在简单系统中,如果从数据库到页面展示都是 POJO 的话,它可以是 DTO;如果从数据库中到业务处理中都是 POJO 的话,它可以是 BO;如果从数据库到整个页面的展示的话,它也可以是 VO.
(七)返回值问题
1.单列多行可以用array接收
dao xml接口
|
<select id=”selectMonthDataByStartAddressAndEndAddress” parameterType=”Map” resultType=”string”>
SELECT DISTINCT DATE_FORMAT(b.creation_time, ‘%m’) AS creationTimeMonth
FROM rests_flight_address a
LEFT JOIN rests_flight_time b ON a.id = b.fight_address_id
WHERE b.status = 0
AND a.start_address = ‘天津’
AND a.end_address = ‘台湾’
AND DATE_FORMAT(b.creation_time, ‘%Y’) = #{year}
</select> |
| —- |
测试类接收代码
String[] strings = this.appUserOrderDao.selectMonthDataByStartAddressAndEndAddress(stringStringHashMap) |
---|
2.用Set接收单列多行参数
ü xml
<select id=”selectStartAddressList” parameterType=”Map” resultType=”String”>
SELECT start_address
FROM rests_flight_address
WHERE **status **= 1
</select>
ü dao接口
Set selectStartAddressList(Map
ü service层
Set set = this.appUserOrderDao.selectStartAddressList(map);
接收的就是去重的集合
(八) 数据库存储日期格式问题
ü 这是简单的
使用varchar
好处:1.后台不需要进行日期到字符串类型的转换2.兼容多数据库切换
缺点:1.如果需要到时间字段进行算法如计算星期DateDiff DateAdd 麻烦.(如果日期字段仅仅用来显示,查找那 么就无所谓)
使用datetime
好处:datetime可以借用sql函数库中运算函数,增加了时间再各种运算上的效率
缺点:后台要进行日期到字符串格式的转换
使用毫秒数
好处:这是一个精准的时间
缺点:1.不直观
2.不方便对时间进行分组查询,比如按月统计,按季度统计
3.要考虑市区因素
ü 下面详细的
将数据库中存储时间的数据类型改为varchar(),这时最好让这些时间是数据库中自动生成的(一个没有格式的输入也可能会导致输出错误),因为存储类型为varchar(),所以获取到的值也就被认为是一个字符串,直接将数据库中的时间字符串进行转化(这时那些转化函数是能识别数据库中的时间函数的),客户端的时间格式不再影响转换过程。
不过数据库中存储时间的类型如果为字符型也会带来一些麻烦:
数据库中的时间仅仅是用来显示、查找的,那么影响还不算大,但如果对时间字段进行一些算法如计算星期、DateDiff、DateAdd等,那就麻烦了,尤其实在大型数据查询中转换类型是会影响效率的
1.总结
数据库中存储日期的字段类型到底应该用varchar还是datetime ?这两种方法各有优势,datetime可以借用sql函数库中运算函数,增加了时间在各种运算上的效率;而varchar类型则可以在字符编码上显出优势。在 存储的时间将来不需要进行大量计算 的前提下,可以考虑选择varchar类型,反之,选择datetime类型。
XML方式开发
(一)基本使用
1.扫描xml文件
扫描resources下的xml文件
mybatis的xml文件在resources文件夹下的mapping文件里面
ü
ü yml配置信息
| mybatis:
mapper-locations: classpath:mapping/.xml |
| —- |
2.mybatis-config.xml 模版
https://blog.csdn.net/wf3612581/article/details/81842590
3.xml配置扫描
4.配置扫描dao的映射位置
## (二)多表操作 ### 1.鉴别器映射 #### 使用场景 https://www.cnblogs.com/yulinfeng/p/6036051.html
鉴别器(discriminator)是MyBatis为我们提供的第三个级联也是最后一个。基于之前两篇级联中的场景,现增加学生们去体检,但男女体检项目不一样,我们把男女体检表做成两张表,当然我想也可以设计为一张表,只有女生的项目男生不填就行了,为了讲解鉴别器就把男女体检表分开。鉴别器的作用在这里就是根据性别的不同去不同的表里进行查询体检情况,例如是男生就在男生体检表里查询,是女生就在女生体检表里查询。
POJO类我们也分为了男生、女生,他们分别继承之前的Student类。
ü
ü
ü 当然这个是我们的使用场景之一
#### 基本使用
### 2.另类配置结果集映射 ü 实体类
public class APPAdvertisingDetail { private BAdvertisementInfoEntity bAdvertisementInfoEntity; private String userName;/上传人/ //**
ü 结果集映射
<resultMap type=”io.project.modules.pile.basic.entity.BAdvertisementInfoEntity” id=”bAdvertisementInfoMap”>
<result property=”id” column=”id”/>
<result property=”userId” column=”user_id”/>
<result property=”advertisementSign” column=”advertisement_sign”/>
<result property=”name” column=”name”/>
<result property=”url” column=”url”/>
<result property=”pictureNum” column=”picture_num”/>
<result property=”uploadTime” column=”upload_time”/>
<result property=”effectTimeBegin” column=”effect_time_begin”/>
<result property=”effectTimeEnd” column=”effect_time_end”/>
<result property=”effectSign” column=”effect_sign”/>
<result property=”updateTime” column=”UPDATE_TIME”/>
</resultMap> <resultMap id=”APPAdvertisingDetailMap” type=”io.project.modules.pile.basic.entity.JsonModel.APPAdvertisingDetail”>
<result property=”userName” column=”userName”></result>
<association property=”bAdvertisementInfoEntity” resultMap=”bAdvertisementInfoMap”></association>
</resultMap>
<select id=”getBAdvertisementInoList” resultMap=”APPAdvertisingDetailMap” >
select ba.*,bu.NAME as userName
from B_ADVERTISEMENT_INFO ba , B_USER bu where ba.USER_ID =bu.ID and ba.USER_ID = #{userId}
and ba.EFFECT_SIGN= #{effectSign} order by ba.UPLOAD_TIME asc
</select>
3.组合实体类建立对应关系
ü 实体类
ü
public class IncidentSchedulingWorkVo implements Serializable {
private EmergencyIncidentEntity emergencyIncidentEntity;
private SchedulingWorkEntity schedulingWorkEntity;
ü dao xml文件
<resultMap type=”io.mergency.entity.Vo.IncidentSchedulingWorkVo” id=”incidentSchedulingWorkVoMap”>
<collection property=”emergencyIncidentEntity” ofType=”io.mergency.entity.EmergencyIncidentEntity” >
<result property=”id” column=”ID”/>
<result property=”gradeId” column=”GRADE_ID”/>
<result property=”kindId” column=”KIND_ID”/>
<result property=”entryTime” column=”ENTRY_TIME”/>
<result property=”summarize” column=”SUMMARIZE”/>
<result property=”triggerLongitude” column=”TRIGGER_LONGITUDE”/>
<result property=”triggerLatitude” column=”TRIGGER_LATITUDE”/>
<result property=”emergencyPlanid” column=”EMERGENCY_PLANID”/>
<result property=”schedule” column=”SCHEDULE”/>
<result property=”state” column=”STATE”/>
<result property=”name” column=”NAME”/>
<result property=”districtId” column=”DISTRICT_ID”/>
</collection>
<collection property=”schedulingWorkEntity” ofType=”io.mergency.entity.SchedulingWorkEntity” >
<result property=”id” column=”ID”/>
<result property=”emergencyIncidentId” column=”EMERGENCY_INCIDENT_ID”/>
<result property=”subordinateTasks” column=”SUBORDINATE_TASKS”/>
<result property=”schedule” column=”SCHEDULE”/>
<result property=”kindId” column=”KIND_ID”/>
<result property=”gradeId” column=”GRADE_ID”/>
<result property=”entryTime” column=”ENTRY_TIME”/>
<result property=”status” column=”STATUS”/>
<result property=”cerealsKind” column=”CEREALS_KIND”/>
<result property=”cerealsName” column=”CEREALS_NAME”/>
<result property=”dispatchNum” column=”DISPATCH_NUM”/>
<result property=”destination” column=”DESTINATION”/>
<result property=”finishTime” column=”FINISH_TIME”/>
<result property=”affiliatedCompanies” column=”AFFILIATED_COMPANIES”/>
<result property=”triggerLongitude” column=”TRIGGER_LONGITUDE”/>
<result property=”triggerLatitude” column=”TRIGGER_LATITUDE”/>
<result property=”radius” column=”RADIUS”/>
</collection>
</resultMap>
多表映射property重复问题
思路: sql使用as别名方式, 配置映射关系时候column就填写别名
<resultMap type=”io.emergency.modules.emergency.entity.Vo.EventsPlanVo”
id=”EventsPlanVoMap”>
<collection property=”emergencyEventsEntity”
ofType=”io.emergency.modules.emergency.entity.EmergencyEventsEntity”>
<result property=”id” column=”ID”/>
<result property=”name” column=”eventsName”/>
<result property=”type” column=”TYPE”/>
<result property=”grade” column=”GRADE”/>
<result property=”districtLevel” column=”DISTRICT_LEVEL”/>
<result property=”longitude” column=”LONGITUDE”/>
<result property=”latitude” column=”LATITUDE”/>
<result property=”description” column=”DESCRIPTION”/>
<result property=”progress” column=”PROGRESS”/>
<result property=”triggeringTime” column=”TRIGGERING_TIME”/>
<result property=”planId” column=”PLAN_ID”/>
</collection>
<collection property=”emergencyPlanEntity”
ofType=”io.emergency.modules.emergency.entity.EmergencyPlanEntity”>
<result property=”id” column=”ID”/>
<result property=”name” column=”planName”/>
<result property=”type” column=”TYPE”/>
<result property=”grade” column=”GRADE”/>
<result property=”districtLevel” column=”DISTRICT_LEVEL”/>
<result property=”description” column=”DESCRIPTION”/>
<result property=”createBy” column=”CREATE_BY”/>
<result property=”createTime” column=”planCreateTime”/>
</collection>
</resultMap>
<select id=”queryEventsPlan” resultMap=”EventsPlanVoMap”>
select e.NAME as eventsName ,p.NAME as planName, p.CREATE_TIME as
planCreateTime
from
EMERGENCY_EVENTS e
left join
EMERGENCY_PLAN p on e.PLAN_ID =p.ID
</select>
4.延迟加载(xml)
就是你需要的时候才查询出来,不需要时候不查询.一对多和多对多的时候建议使用延迟加载
重点:需要在Mybatisconfig.xml配置文件里面告诉框架我需要开启延迟加载:
Mybatisconfig.xml
<settings>
<setting name=”lazyLoadingEnabled” value=”true”/>
<setting name=”aggressiveLazyLoading” value=”false”/>
<setting name=”cacheEnabled” value=”true”/>
</settings>
ü IUserDao.xml配置文件
<resultMap type=”user” id=”userMap”>
<id column=”id” property=”id”/>
<result column=”username” property=”username”/>
<result column=”sex” property=”sex”/>
<result column=”birthday” property=”birthday”/>
<result column=”address” property=”address”/>
<!— 配置实体中关联的对象是一个集合的标签: collection
property:用于指定属性的名称(是你resultMap 的type属性的实体类里面的变量名)
javaType:用于指定属性的类型<br /> ofType:用于指定集合元素的类型(是集合里面泛型的值)
select:用于指定查询账户的唯一标识(全限定类名+方法名)<br /> column:用于指定使用哪个字段的值作为查询条件<br /> (对应select="com.itheima.dao.IAccountDao.findByUid" 标签里面的查询条件)
—>
<!—延迟加载配置操作(不需要封装第二张表的对应关系)
property: 写user表里面的list集合的 变量名 (该list集合指向着别的实体类,一对多关系)
ofType 写对面的返回值类型的泛型的值<br /> .--><br /> <**collection property="accounts" ofType="account"<br />**<br /> **select="com.itheima.dao.IAccountDao.findByUid"<br />**<br /> **column="id"<br />**<br /> /><br /></**resultMap**>
<select id=”findAll” resultMap=”userMap”>
select _ _from user ;
</*select>
ü IAccountDao.xml 配置文件
<select id=”findByUid” resultType=”account” >
select _ _from account a where uid = #{uid};
</*select>
别忘了在interface里面写对应的接口,配置文件里面有的接口也要有,不然无法使用
如果是多对多的怎么写?
多对多配置就相当于一对多
resultMap=“BaseResultMap” >
SELECT * FROM sys_user_role sur INNER JOIN sys_role sr ON sur.roleId=sr.id
AND userid = #{id}
(三)配置项目
1.扫描dao接口
//在启动类配置@MapperScan 注解即可配置扫描dao层接口带@Mapper注解的
@MapperScan({“cn.stylefeng.guns.modular.system.mapper”, “cn.stylefeng.guns.modular.demos.dao”})
Mybatis使用
(一)批量删除
ü 通过list集合批量删除
Service层
/* checkboxSingle是前台页面CheckBox多选框传来的一个String字符串 是用”,”号把多个选中的id进行拼接的String字符串 /
public Integer deleteAllByCheckbox(String checkboxSingle) {
// 切割成数组
String[] checkboxArr = checkboxSingle.split(**","**);
// Arrays.asList把数组转集合
List<String> checkboxList = Arrays.asList(checkboxArr);
**return this**.**userDao**.deleteAllByCheckbox(checkboxList);
}
Dao接口
Integer deleteAllByCheckbox(List
Dao 的映射文件
<!— 根据list集合批量删除员工信息
SQL类似:DELETE FROM exam
.questions
WHERE questionId
IN (‘10’,’11’,’8’,’9’)
—>
<delete id=”deleteAllByCheckbox” parameterType=”java.util.List”>
DELETE FROM personlmxi WHERE user_id in
<foreach collection=”list” item=”id” separator=”,” open=”(“ close=”)”>
#{id}
</foreach>
</delete>
(二)主表外键指向的是实体类而不是字段
(三)封装json格式数据
需要这种格式的数据:
$scope.brandList={data:[{id:1,text:’联想’},{id:2,text:’华为’},{id:3,text:’小米’}]};
我们后台就需要提供一下
前台JS数据
ü controller层js文件
/提供品牌集合 (初始化,相当于java的new对象)/
$scope.brandList={
data:[]
};
$scope.findBrandList=function(){
brandService.selectOptionList().success(
function(response){
$scope.brandList.data=response;
}
);
}
ü service层js文件
this.selectOptionList=function(){
return $http.get(“../brand/selectOptionList.do”);
}
后台java代码
ü Controller层
/return $http.get(“../brand/selectOptionList.do”);
需要为后台提供一下json个数数据:
{data:[{id:1,text:’联想’},{id:2,text:’华为’},{id:3,text:’小米’}]};
所以返回值类型就用List</
@RequestMapping(“/selectOptionList.do”)
public Listreturn brandService.selectOptionList();
}
ü service层
@Override
public Listreturn this*.brandMapper.selectOptionList();
}
ü dao层
List {data:[{id:1,text:’联想’},{id:2,text:’华为’},{id:3,text:’小米’}]};
Mybatis多表操作
(一)注解开发方式
1.一对多建立关系
/
查询所有用户
/
@Select(“select * from user”)
//id是提供别人引用的, value是配置映射关系的
@Results(id=”userMap”,value= {
//id=true代表的是主键
@Result(id=true,column=”id”,property=”userId”),
@Result(column=”username”,property=”userName”),
@Result(column=”sex”,property=”userSex”),
@Result(column=”birthday”,property=”userBirthday”),
@Result(column=”address”,property=”userAddress”),
//这里是一对多的桥梁, column是数据库一的一方的指向多的一方的主键.
//property 是一的一方的实体类的集合的名字
@Result(column=”id”,property=”accounts”,
//代表配置一对多关系
many=@Many(select=”com.itheima.dao.IAccountDao.findByUid”,fetchType=FetchType.LAZY**)
)
})
List
2.多对一建立关系
/
查询所有账户
/
//多对一
@Select(“select * from account”)
//id可以供别人引用的
@Results(id=”accountMap”,value= {
@Result(id=true,column=”id”,property=”id”),
@Result(column=”uid”,property=”uid”),
@Result(column=”money”,property=”money”),
//注意这里,这里是连接多对一的桥梁, column 是数据库多的一方的外键
property里面的user是一的一方的实体类的名字
两个在数据库是对应关系
@Result(column=”uid”,property=”user”,
// one 代表是多对一关系 one=@One(select=”com.itheima.dao.IUserDao.findById”,fetchType=FetchType.LAZY**)
)
})
List
(二)表关系概念
ü 表的关系有几种?(四种)
一对一(人和身份证,人是主,身份证是从,先有人才有身份证)
一对多 (用户和订单)
多对一 (一对多和多对一是两个概念,一个用户有多个订单,但是一个订单不可 能有多个用户)
多对多(比如老师和学生)
ü 数据库中实现表关系
一对多的实现:
使用外键。
把一的一方称为主表
把多的一方称为从表
外键是指:
从表中有一列,该列的取值来源于主表的主键或者是null。
一对一的实现:
特殊的一对多。
一对一也有主从关系
使用外键,但是有两种方式:
第一种:新建一个字段,让该字段成为外键,并且必须有非空和唯一约束。
第二种:使用从表的主键,让他成为外键。
多对多的实现:
使用中间表
中间表应该包含两个表的外键
实体类中体现两个实体的关系:
一对一:
两个实体各自包含对方一个对象引用
一对多:
主表实体包含一个从表实体的集合引用
从表实体包含一个主表实体的对象引用
多对多:
三张表两个实体
两个实体各自包含对方一个集合引用
mybatis的多表:
一对多的示例:
用户和账户
一个用户可以开多个账户
多个账户属于一个用户
mybatis特殊:
它是把多对一看成了一对一。
自己总结:
在企业中看一个表的关系先看外键.有外键就是从表,如果外键有非空不为null就是一对一,否则就是一对多.如果有中间表就是多对多