**
斜体样式MyBatis概念**
MyBatis是一个实现了JPA规范的用来连接数据库并对其进行增删改查操作的开源框架 (就和传统的JDBC一样,就是个连接数据库的东西),其实,它底层就是一个JDBC封装的组件。MyBatis的前身是Ibatis,Ibatis创建与2002年最初为Apache下面的一个开源项目,2010迁移到google code下面并改名为MyBatis。
是一个持久层的框架,在Apache下的项目
是一个ORM框架(Object relationship mapper)
Mybatis是一个半自动化的ORM框架 因为程序员需要自己写sql语句mybatis做了参数的输入映射 和 结果集的输出映射 因此 比较灵活 适用范围也比较大
MyBatis的优点
简单易学,容易上手(相比于Hibernate) ---- 基于SQL编程
消除了JDBC大量冗余的代码,不需要手动开关连接
很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持,而JDB提供了可扩展性,所以只要这个数据库有针对Java的jar包就可以就可以与MyBatis兼容),开发人员不需要考虑数据库的差异性。
提供了很多第三方插件(分页插件 / 逆向工程)
能够与Spring很好的集成
mybatis 跟springmvc进行整合
核心配置文件:sqlMapConfig.xml解读
组成部分:
头部:<?xml version="1.0" encoding="UTF-8"?> //版本声明
这里是远程的约束文件
Mybatis的使用转变成mapper代理开发的的演变 //dtd约束文件
体:
配置使用什么管理事物这里选择的是jdbc
<property name="url" value="jdbc:mysql://localhost:3306/bbsmsg"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
设置数据源
配置运行环境
配置默认的运行环境
映射文件的解读:user.xml解读
头部:
<!-版本声明,加dtd约束–>
<!-- id 标识映射文件中的sql语句 唯一
parametertype 指定输入参数的类型
#{}占位符
name: 表示的是接收的输入参数 如果参数是简单类型 参数名 任意 但是如果参数是pojo类型 那么 #{属性名}
resultType :指定 select语句返回的结果的类型 无论sql返回的是单条 还是多条结果集 resultType 的是单条映射的对象
-->
<select id="findByName" parameterType="String" resultType="com.bean.ly.User">
select * from user where username=#{name}
</select>
<!--
${}拼接sql语句,模糊查新的时候会用到,将输入参数的值 不加任何修饰符并拼接到sql语句中 ======》会引发sql注入
如果接收的参数的类型是简单类型 那么参数名必须是value
如果接收的参数类型是pojo实体类 那么参数名可以是属性名
-->
<select id="findUnameLike" parameterType="String" resultType="com.bean.ly.User">
select * from user where username like '%${value}%'
</select>
### 获取到 新增的id并返回
第一种方式:
<!-- 新增 msg -->
<insert id="insertMsg" parameterType="com.bean.ly.Msg">
<selectKey keyProperty="msgid" order="AFTER" resultType="int">
select LAST_INSERT_ID()
</selectKey>
insert into msg (username,title,msg_create_date) values(#{username},#{title},#{msg_create_date})
</insert>
</mapper>
第二种方式
加上useGeneratedKeys和keyProperty配置即可,前者是指设置是否使用jdbc的getGenereatedKeys方法获取主键并赋值到keyProperty设置的属性中,后者即实体类主键字段
<insert id="insertPassengers" useGeneratedKeys="true" keyProperty="id" parameterType="Passenger">
insert into t_passenger values(null,#{name},#{sex},#{phoneNum},#{credentialsType},#{credentialsNum},#{travellerType})
</insert>
然后 在service 调用的时候
mapper.insertPassengers(passenger2);
System.out.println(passenger2.getId());
config配置文件详解
4.1全局配置文件的内容和顺序
Properties(属性)
Settings(全局参数设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境信息集合)
environment(单个环境信息)
transactionManager(事务)
dataSource(数据源)
mappers(映射器)
属性解析
properties标签:
Mybatis可以通过改标签来读取java配置信息:
dataSourse数据源里面的四大属性 可能变动而且后期维护不易修改 因此需要一个专门的设置这四个属性的properties文件 叫 db.properties 里面的内容如下
user=root
url=jdbc:mysql://localhost:3306/bbsmsg
password=4321
driver=com.mysql.jdbc.Driver
就是用来设置四大属性的值
Properties ps=new Properties();
InputStream inputStream=new FileInputStream("./src/db.properties");
ps.load(inputStream);
System.out.println(ps);
ps.setProperty("password", "4321");
System.out.println(ps);
PrintStream os =new PrintStream("./src/db.properties");
ps.list(os);
在这样一个方法中可以查看和修改 db.properties的值
然后在configuration标签中加入
这样就不用再sqlMapConfig.xml中进行修改操作了
配置中需要注意的几点
1)在properties元素体内定义的属性首先被读取
2)然后会读取properties元素中resource或url加载的属性,它会覆盖已读取的同名属性
3)最后读取parameterType传递的属性,会覆盖已读取的同名属性
因此,通过parameterType传递的属性具有最高优先级,resource或url加载的属性次之,最低优先级的是properties元素体内定义的属性
建议:
不在properties元素体内添加任何属性,只将属性值定义在properties文件中。
在properties文件中定义属性名要有一定的特殊性,如:XXX.XXX.XXX
Settings
设置log4j的格式
typeAliases(类型别名)
类型别名是 Java 类型的简称。
它仅仅只是关联到 XML 配置,简写冗长的 JAVA 类名。例如:
<!—这是单个设置别名 -->
<!—这是批量设置别名 别名为类名–>
typeHandlers(类型处理器)
mybatis中通过typeHandlers完成jdbc类型和Java类型的转换
通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义类型处理器
当然 也可以自定义 新的类型 但是没必要
UserMapper.xml
==========================
输入映射:
通过parameterType完成 支持 简单类型,hashmap pojo,包装类型
输出映射:
使用resultType 完成输出映射,需要满足查询结果集中的列名和实体类中的列明保持一致才可以赋值成功 如果不一致 也不会报错 但是 会为null值
如果查询结果全部不一致 那么就不会创建对象
输出简单类型:
一般用于聚合运算(尽量少用*)
返回的结果是一行 和一列的时候
输出对象:
使用resultMap完成高级的结果映射
当我们的查询的结果和实体列的属性名不一致的时候我们可以在使用resultMap完成结果集和对象的映射
部分解释见==》映射文件的解读:user.xml解读 上文
<?xml version="1.0" encoding="UTF-8"?> ### 动态sql wher 标签动态生成条件 select 语句中 可以有where 标签 用作条件判断 并且会删除第一个and sql 片段 在mapper文件中有一个 sql标签 是叫做 sql片段 可以在里面写好sql片段 在整个文件中都可以调用这个sql片段foreach
foreach标签 里面可以设置 以什么开始 和以什么结束 和以什么分割达到完善sql语句的目的
实例
根据用户传入的参数是否存在 来生成相应的sql语句
select * from user
and sex=#{user.sex}
</if>
<if test="orders!=null">
<if test="orders.id!=null">
and id=(select user_id from orders where id=#{orders.id})
</if>
</if>
</where>
</select>
上面这段代码: 参数 userqueryvo中的两个对象的参数 可以选择性的传入 通过if判断来确定最终生成的sql语句来达到动态sql的效果
然后 在mapper文件中有一个 sql标签 是叫做 sql片段 可以在里面写好sql片段 在整个文件中都可以调用这个sql片段
代码如下:
<select id="findBySexAndId" parameterType="userqueryvo" resultMap="userResultMap">
select * from user
<where>
<include refid="-"></include>
</where>
</select>
<sql id="userWhere">
<if test="user!=null">
<if test="user.sex!=null">
and sex=#{user.sex}
</if>
</if>
<if test="orders!=null">
<if test="orders.id!=null">
and id=(select user_id from orders where id=#{orders.id})
</if>
</if>
</sql>
<select id="findUserByIds" parameterType="uservo" resultMap="userResultMap">
select * from user
<where>
<if test="ids!=null">
<foreach collection="ids" open="(" close=")" item="id" separator="OR">
id=#{id}
</foreach>
</if>
</where>
</select>
高级结果映射
一对一
在多方对象中添加一个属性 (外键)相当于数据库中的外键
这个属性是一方的类型
-
使用resultType 如果没有过多的需求建议使用这一种 需要保持列名和结果集的列名一致 新建一个继承字段多的,添加需要的字段
-
使用resultMap(可以实现懒加载)
a) 在一方添加另一方对象作为属性
b) 定义resultMap 使用association
比如在orders中 添加user成员属性
public class Orders {
private Integer id;
private Integer user_id;
private String number;
private Date createtime;
private String note;
private User user;
然后在mapper.xml文件中设置结果集映射 将查询出来的 对象属性传入结果对象(order)的属性(就是该对象user)
SELECT a.id oid,a.user_id,a.createtime,a.note,a.number,b.id uid,b.username,b.birthday,b.address,b.sex from orders a,user b where a.user_id=b.id<!-- association :将映射结果 映射为类中 的一个对象 property:将要映射为的对象在orders中的属性名 javaType:将要映射为对象的类名 --> <association property="user" javaType="user"> <id column="id" property="id"/> <result column="birthday" property="birthday"/> <result column="address" property="address"/> <result column="sex" property="sex"/> <result column="username" property="username"/> </association>
一对多
在一方添加集合对象作为属性
定义resultMap 使用collection进行对集合对象赋值
select a.id uid,a.address,a.username,b.id oid,b.number,b.createtime from user a,orders b where a.id=b.user_id
多对多
第一步 先要定义类与类之间的关习 根据 数据库中表之间的关系来确定类与类之间(如在user中添加 List 在orders中添加List然后在Orderdatils中 添加Items属性 来确定类和类之间的联系)的关系
第二步 通过resultMap实现结果集的映射多对多 实质上就是一对多的层层嵌套
select a.id uid,a.username,a.address,b.id oid,c.id cid,c.items_id,c.items_num,d.name
,d.detail,d.price from user a,orders b,orderdetail c,items d where a.id = b.user_id and b.id = c.orders_id and c.items_id = d.id;
延迟加载
使用resultMap完成懒加载(association collection 具有延时加载的功能)
懒加载(没有执行获取方法 比如orders,getUser那么对应的sql片段就不会执行 来实现懒加载)
首先 在sqlMapConfig.xml里面 的settings标签里面设置懒加载开启 和积极加载关闭
然后在userMapper.xml里面配置 resutMap和sql语句 实现懒加载
select id oid,user_id,createtime,note,number from orders
select id uid,username,birthday,address,sex from user where id=#{id}
然后在biz层调用的时候如果不调用getUser方法那么就不会执行查询用户信息的sql语句
@Test
public void testMapper1(){
SqlSession ss=sf.openSession();
UserMapper mapper=ss.getMapper(UserMapper.class);
List user=mapper.findOrdersAndUsers();
for (Orders orders : user) { System.out.println(orders.getId()+"="+orders.getOrderdetails()+""+orders.getCreatetime());
System.out.println(orders.getUser()+“user”);
}
ss.close();
}
查询缓存
缓存:储存已经加载的数据 当再次需要同样的数据的时候会直接从缓存里面获取,mabatis默认自动开启一级缓存 二级缓存需要手动开启 缓存用于解决服务器的压力 提高数据库的性能
一级缓存:
基于session的缓存 在session范围内生效但是在更新操作之后会清空缓存
缓存的实现 : 就是当要查询的数据在缓存中存在的时候那么mybatis不会执行sql语句来缓解服务器的压力
二级缓存
范围是Mapper级别的范围
区别:
一级缓存和二级缓存
一级缓存基于SqlSession级别的缓存。当我们操作数据库的时候,需要创建sqlSession对象,当前对象中有hashmap用户存储数据,不同de
sqlSession之间该区域不共享。
二级缓存给予mapper级别的缓存,多个sqlSession区执行mapper中的同一个sql语句的时候,这多个sqlSession共享二级缓存。
一级缓存 演示;
@Test
public void testMapper11(){
SqlSession ss=sf.openSession();
UserMapper mapper=ss.getMapper(UserMapper.class);
User user=mapper.findUserById(10);
System.out.println(user+"=======11");
User user2=mapper.findUserById(10);
System.out.println(user2+"======2");
ss.close();
}
Created connection 951880373.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@38bc8ab5]
> Preparing: select id uid,username,sex,birthday from user where id=?
> Parameters: 10(Integer)
< Columns: uid, username, sex, birthday
< Row: 10, 张三, 1, 2014-07-10
< Total: 1
User [uid=10, username=张三, birthday=Thu Jul 10 00:00:00 CST 2014, sex=1, address=null, orders=null]=======11
User [uid=10, username=张三, birthday=Thu Jul 10 00:00:00 CST 2014, sex=1, address=null, orders=null]========2
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@38bc8ab5]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@38bc8ab5]
Returned connection 951880373 to pool.
这里只执行了一次 sql查询 第二次 是咋缓存里读取的数据
下面执行更新操作之后 就需要重新读取书数据
一个session close之后也需要重新读取数据
@Test
public void testMapper11(){
SqlSession ss=sf.openSession();
UserMapper mapper=ss.getMapper(UserMapper.class);
User user=mapper.findUserById(80);
System.out.println(user+"=======11");
mapper.deleteUserById(80);
ss.commit();
User user2=mapper.findUserById(80);
System.out.println(user2+"========2");
ss.close();
}
mybatis分页插件
mybatis分页插件
1.导包:pagehelper jsqlparaser
2.配置文件:在applicationContext-dao 里面的sqlSessionFactory bean中添加一个属性(死代码 粘贴进去就行)
helperDialect=mysql
reasonable=true
然后在需要分页的功能上就不用list了 需要用pageInfo 这个返回值
service层的写法实例
public PageInfo getProducts(Integer pageCode,Integer pageSize) {
// TODO Auto-generated method stub
PageHelper.startPage(pageCode,pageSize);
//在调用startPage之后调用原来的方法此时就已经变成了分页的查询了
List<Product> list=mapper.getProducts();
PageInfo<Product> pageInfo=new PageInfo<Product>(list);
return pageInfo;
}
controller层的写法示例
controller 这里 主要是对参数 进行设置过滤 设置默认值 和是否必填 和 形参的映射 用pageInfo进行接收
@RequestMapping("/findAll.action")
public String showPro(@RequestParam(value=“pageCode”,defaultValue=“1”,required=true)Integer pageCode,
@RequestParam(value=“pageSize”,defaultValue=“2”,required=true)Integer pageSize,ModelMap model){
PageInfo<Product> list=service.getProducts(pageCode,pageSize);
model.addAttribute("pageInfo",list);
return "/product/list";
}
SpringBoot PageHelper
1.引入依赖
-
<dependency>
-
<groupId>com.github.pagehelper</groupId>
-
<artifactId>pagehelper-spring-boot-starter</artifactId>
-
<version>1.2.10</version>
-
</dependency>
2.在application.yml中做如下配置
-
分页配置
- pagehelper:
- helper-dialect: mysql
- reasonable: true
- support-methods-arguments: true
- params: count=countSql
3.在代码中使用 -
PageHelper.startPage(pageNum,pageSize);//这行是重点,表示从pageNum页开始,每页pageSize条数据
-
List<Tools> list = toolsMapper.findAll();
-
PageInfo<Tools> pageInfo = new PageInfo<Tools>(list);
-
return ServerResponse.createBySuccess("查询成功",pageInfo)
pageHelper中的属性
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//排序
private String orderBy;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List list;
//第一页
private int firstPage;
//前一页
private int prePage;
//下一页
private int nextPage;
//最后一页
private int lastPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
生成日期格式化(yyMMddHHmmssSS)的字符串
一般用于产品或者订单的编号的自动生成(ss秒SSS毫秒)
String date = new java.text.SimpleDateFormat(“yyyyMMddHHmmssSSS”).format(new java.util.Date());
String OrderNum=“ONM-”+date;
mybatis逆向工程生成bean
需要:运行生成的 一个类 ,config.xml配置文件 (配置映射和生成的位置),三个jar包
运行的类:
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
public class Test011 {
public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
// TODO Auto-generated method stub
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指向逆向工程配置文件
File configFile = new File("config.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
}
config.xml
<?xml version="1.0" encoding="UTF-8"?><context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 取消注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssmdb" userId="root"
password="1234">
</jdbcConnection>
<!-- -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 指定javabean生成的位置 -->
<javaModelGenerator targetPackage="com.yh.bean"
targetProject=".\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 指定sql映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.yh.mapper" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定dao接口生成的位置,mapper接口 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.yh.mapper" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定每个表的生成策略 -->
<table tableName="t_log" domainObjectName="Tlog">
</table>
<table tableName="t_member" domainObjectName="Member"></table>
<table tableName="t_order" domainObjectName="Order"></table>
<table tableName="t_passenger" domainObjectName="Passenger"></table>
<table tableName="t_product" domainObjectName="Product"></table>
<table tableName="t_role" domainObjectName="Role"></table>
<table tableName="t_permission" domainObjectName="Permisson"></table>
<table tableName="t_user" domainObjectName="User"></table>
</context>
- jar包
复杂sql语句参数判空
说明:针对输入参数为简单类型#{}中可以是任意类型,判断参数是否为空要用 _parameter(它属于mybatis的内
置参数) 简单类型才能用这个 比如String 类型的参数 不然会报错
and a.courseid=#{courseId}
复杂类型的用上面的方法 直接 调用对象里的属性就可以