一、mybatis概述
1.1、mybatis
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
mybatis就是一个封装来jdbc的持久层框架,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。它和hibernate都属于ORM框架,但是具体的说,hibernate是一个完全的ORM框架,而mybatis是一个不完全的ORM框架。
1.2、JDBC代码
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.创建连接数据库对象
Connection conn=DriverManager.getConnection( "jdbc:mysql://localhost:3306/test4?characterEncoding=utf-8","root","root");
// 3.创建操作数据库对象
Statement stat=conn.createStatement();
// 4.编写sql语句
String sql =" select name from user ";
// 5.执行sql语句,返回结果集
ResultSet rs=stat.executeQuery(sql);
// 6.操作结果集
while(rs.next()) {
System.out.println(rs.getString("name"));
}
// 7.释放资源
rs.close();
stat.close();
conn.close();
JDBC 存在的问题:
- 创建连接时存在硬编码
- 在执行statement时存在硬编码
- 不停的连接关闭数据库,会使数据库性能降低
1.3、mybatis框架核心配置
- mybatis配置文件
- mybatis通过读取配置文件,构造出SqlSessionFactroy,即会化工厂。
- 通过SQLSessionFactroy,可创建SQLSession
- SQLSession不能操作数据库的,通过底层的executor执行器接口来操作数据库
- executor执行器要处理的SQL信息是封装到一个底层对象mapperStatement中
二、mybatis入门
1、建库建表
2、操作入门
1)操作思路
- 创建entity类
- 创建配置文件SQLmapconfig.xml
- 编写映射文件
- 加载映射文件,在SQLmapconfig.xml中进行加载
- 编写测试
思路:
a)读取配置文件;
b)通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
c)通过SqlSessionFactory创建SqlSession。
d)调用SqlSession的操作数据库方法。
关闭SqlSession。
mybatis参考文档
2)创建entity实体和DAO接口
public class Emp {
private Integer id;
private String name;
private String age;
private String address;
private Date birthday;
//创建DAO接口
public interface EmpMapper {
public Emp selectById(Integer id);
}
3)在pom.xml中加入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--mysql连接jar-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
</dependencies>
4)创建sqlmapconfig.xml文件
可参考mybatis配置文件
<?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>
<!--配置数据库连接-->
<environments default="ppbb">
<environment id="ppbb">
<!--以JDBC的方式提交事物 自动提交 -->
<transactionManager type="JDBC"></transactionManager>
<!--POOLED 表示使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test4? characterEncoding=utf-8"></property>
<property name="username" value="root"></property> <property name="password" value="root"></property> </dataSource>
</environment>
</environments>
</configuration>
5)创建mapper映射文件
可参考mybatis配置文件
<?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:命名空间,给这个mapper.xml在项目取个名字 名称和对应接口的全限定名一致 -->
<mapper namespace="com.mapper.EmpMapper">
<!--
id:这个标签的名称,唯一标示 和对相应接口的方法名一致
parameterType:入参的类型
resultType:出参类型 返回值类型
resultMap:配置返回值 实体类和表中的类型不匹配的时候
-->
<select id="selectById" parameterType="int" resultType="Emp" resultMap="empResultMap" >
select * from emp where id = #{id}
</select>
</mapper>
6)加载映射文件
<mappers>
<!--加载单个xml-->
<mapper resource="mapper/EmpMapper.xml" />
<!--加载整个包下的xml文件 接口和xml文件的路径要一直 -->
<!--<package name="com.whhp.k9501.mapper.*" />-->
</mappers>
7)测试
public class EmpDemo {
@Test
public void selectByIdTest() throws IOException {
//加载sqlMapConfig.xml文件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取文件输入流
InputStream inputStream = Resources.getResourceAsStream("mybatis/SqlMapConfig.xml"); //创建sqlSession工厂
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//生产
sqlsession SqlSession sqlSession = sqlSessionFactory.openSession();
//创建mapper接口的实例
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//使用接口
Emp emp = mapper.selectById(18);
System.out.println("emp = " + emp);
//关闭sqlsession
sqlSession.close();
}
}
三、单表的CRUD
四、入参和出参的类型
1、入参的类型
入参的参数类型
基本数据类型 String和基本数据类型的用法一致
案例:删除数据
Object对象
添加和修改
获取对象中的参数方式:#{属性名称} 对象必须是JavaBean
- map
获取数据的方式:#{键}
//参数为map的方法
List<Emp> selectEmpMap(Map map);
<!--参数为map集合-->
<select id="selectEmpMap" parameterType="java.util.Map" resultType="Emp">
select * from emp where age >= #{age} and address = #{address}
</select>
// 参数为map集合
@Test
public void selectEmpMapTest(){
SqlSessionFactory sqlSessionFactory = DBUtil.getSqlSessionFactory("mybatis/SqlMapConfig.xml");
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//模拟数据
Map<String,Object> map =new HashMap<>();
map.put("age",18); map.put("address","蜀国");
//调用方法
List<Emp> empList = mapper.selectEmpMap(map);
for (Emp emp : empList) {
System.out.println("emp = " + emp);
}
}
- list
获取方式:foreach去迭代list集合,产生的元素
元素是引用数据类型时,参数获取方法#{obj.属性名}
元素是基本数据类型时,参数获取方法#{obj}
//添加多条数据
int saveEmpList(List<Emp> empList);
<!--多条添加-->
<insert id="saveEmpList" parameterType="java.util.List">
insert into
emp(name,age,address,birthday)
values
<foreach collection="list" item="emp" separator=",">
(#{emp.name},#{emp.age},#{emp.address},#{emp.birthday})
</foreach>
</insert>
// 参数为List集合
@Test
public void saveEmpListTest(){
SqlSessionFactory sqlSessionFactory = DBUtil.getSqlSessionFactory("mybatis/SqlMapConfig.xml");
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//模拟数据
List<Emp> empList =new ArrayList<Emp>();
empList.add(new Emp("诸葛亮",23,"襄阳",new Date(2500000)));
empList.add(new Emp("诸葛瑾",28,"南京",new Date(60000000000L)));
empList.add(new Emp("庞统",35,"成都",new Date(5246843218L)));
empList.add(new Emp("周瑜",18,"南京",new Date(250000)));
//调用方法
int i = mapper.saveEmpList(empList);
sqlSession.commit();
System.out.println("i = " + i);
}
- 数组
获取方式:foreach去迭代数组,产生的元素
参数获取方式:#{item}
//删除多条多条数据
int deleteEmpAraay(int[] ids);
<!--deleteEmpAraay删除多条-->
<delete id="deleteEmpAraay" >
delete from emp where id
in
<foreach
collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
// 多条删除
@Test
public void deleteEmpArrayTest(){
SqlSessionFactory sqlSessionFactory = DBUtil.getSqlSessionFactory("mybatis/SqlMapConfig.xml");
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//模拟数据
int[] ids ={45,47,50,3};
//调用方法
int i = mapper.deleteEmpAraay(ids);
sqlSession.commit();
System.out.println("i = " + i);
}
2、出参的类型
resultType,resultMap 返回值的类型 Emp
返回值是单条数据
emp
返回值是多条数据
List
区别:
如果实体类属性和表中的字段 无法直接匹配时我们就使用resultMap处理属性
<!--配置返回值类型-->
<!--实体类型-->
<resultMap id="bookResultMap" type="Book">
<!--与表中主键相对应的属性配置-->
<id property="bookId" column="book_id"></id>
<!--其他对相应的属性-->
<result property="bookName" column="book_name"></result>
<result property="bookAuthor" column="book_author">
</result> <result property="bookSaleTime" column="book_saleTime" javaType="java.sql.Date"></result>
<result property="bookPrice" column="book_price"> </result>
<result property="bookType" column="book_type"></result>
</resultMap>
<!--查询全部-->
<select id="selectBookAll" resultMap="bookResultMap">
select * from book
</select>
3、分页查询
需要两个参数
起始数,每页条数
使用map集合作为参数
//分页查询
List<Book> selectBookAllFY(Map map);
<!--分页-->
<select id="selectBookAllFY" parameterType="java.util.Map" resultMap="bookResultMap">
select * from book limit #{start} , #{size}
</select>
//分页测试
@Test
public void selectEmpAllFYTest() throws IOException { //获取配置文件输入流
SqlSessionFactory sqlSessionFactory = DBUtil.getSqlSessionFactory("mybatis/SqlMapConfig.xml");
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取BookMapper的实现类对象
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
//模拟参数
Map map = new HashMap<String,Object>();
map.put("start",0); map.put("size",5);
//调用分页查询
List<Book> bookList = bookMapper.selectBookAllFY(map);
for (Book book : bookList) {
System.out.println("book = " + book);
}
}
4、条件查询
//条件查询
List<Book> selectBookAllTJ(Map map);
<!--条件查询 书名,价格范文 -->
<select id="selectBookAllTJ" parameterType="java.util.Map" resultMap="bookResultMap">
select * from book where 1=1
<if test="name != null"> and book_name like '%${name}%' </if>
<if test="priceFrom!=null"> and book_price >= #{priceFrom} </if>
<if test="priceTo !=null"> and book_price <= #{priceTo} </if>
</select>
// 条件查询
@Test
public void selectEmpAllTJ(){
//获取配置文件输入流
SqlSessionFactory sqlSessionFactory = DBUtil.getSqlSessionFactory("mybatis/SqlMapConfig.xml"); SqlSession sqlSession = sqlSessionFactory.openSession();
//获取BookMapper的实现类对象
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
//模拟数据
Map map = new HashMap<String,Object>();
map.put("name","鹿"); map.put("priceFrom",100);
map.put("priceTo",200); List<Book> bookList = bookMapper.selectBookAllTJ(map);
for (Book book : bookList) {
System.out.println("book = " + book);
}
}
小结:
parameterType和resultType
parameterType指定输入参数的java类型,可以填写别名或Java类的全限定名。
resultType指定输出结果的java类型,可以填写别名或Java类的全限定名。
#{}和${}
#{}:相当于预处理中的占位符?。
#{}里面的参数表示接收java输入参数的名称。
#{}可以接受HashMap、POJO类型的参数。
当接受简单类型的参数时,#{}里面可以是value,也可以是其它。
#{}可以防止SQL注入。
${}:相当于拼接SQL串,对传入的值不做任何解释的原样输出。
${}会引起SQL注入,所以要谨慎使用。
${}可以接受HashMap、POJO类型的参数。
当接受简单类型的参数时,${}里面只能是value。
selectOne和selectList
selectOne:只能查询0或1条记录,大于1条记录的话,会报错:
selectList:可以查询0或N条记录
五、多表查询
1.案例:用户和订单
user和orders:
User 与orders:一个用户可以创建多个订单,一对多
Orders 与 user:多个订单只由一个用户创建,多对一
orders和orderdetail:
Orders 与 orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系
orderdetail 与orders:多个订单明细包括在一个订单中, 多对一
orderdetail和items:
Orderdetail 与 items:多个订单明细只对应一个商品信息,多对一
Items 与 orderdetail:一个商品可以包括在多个订单明细 ,一对多
需求:
根据商品ID查找订单信息,包括用户名和地址
#查找id为6的所有定单
SELECT
orders.id, orders.number,orders.createtime,orders.note,`user`.username,`user`.address
FROM
orders ,`user`
WHERE
orders.user_id = `user`.id AND `user`.id = 6;
- 一对一
1)一对一resultType实现
复杂查询时,单表对应的实体类已不能满足输出结果集的映射。所以要根据需求建立一个扩展类来作为resultType的类型。
1.1、创建一个订单的扩展类
public class OrdersExt extends Orders{
private String username;
private String address;
//set和get略
}
1.2、声明一接口
public interface OrdersMapper{
public OrdersExt findOrdersUser(int ordersId);
}
1.3、编写配置文件
<?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:命名空间,给这个mapper.xml在项目取个名字 名称和对应接口的全限定名一致 -->
<mapper namespace="com.mapper.OrdersMapper">
<sql id="select_orders">
orders.id,
orders.number,
orders.createtime,
orders.note,
</sql>
<sql id="select_user">
user.username,
user,address
</sql>
<select id="findOrdersUser" parameterType="int" resultType="ordersExt">
select
<include refid="select_ordrs"/>
<include refid="select_user"/>
from
ordrs o,user s
where
o.id=s.id
and
o.id=#{?};
</select>
</mapper>
1.4、加载映射文件
<mappers>
<mapper resource="com/mapper/OrdresMapper.xml"/>
</mappers>
1.5、测试
//测试
@test
public void test1 throws Exception {
SqlSession session = ssf.openSession();
//通过session获取代理
OrdresMapper ordresMapper = session.getMapper(OrdresMapper.class);
OrdersExt ordersExt = ordersMapper.findOrdersUser(3);
Sysout.out.prinln(ordersExt );
session.close;
}
2)一对一resultMap实现
掌握association的使用
2.1、编写接口
public Orders findOrdersBylMap(int ordersId);
2.2、编写配置文件
<resultMap type="orders" id="ordersResulMap">
<result colum="id" property="id"/>
<result colum="number" property="number"/>
<result colum="createtime" property="createtime"/>
<result colum="note" property="note"/>
<!--关联内部对象-->
<association property="user" javaType="com.entity.User">
<result colum="username" property="username"/>
<result colum="address" property="address"/>
</association>
</resultMap>
<select id="findOrdersBylMap" parameterType="int" resultMap="ordersResulMap">
select
<include refid="select_ordrs"/>
<include refid="select_user"/>
from
ordrs o,user s
where
o.id=s.id
and
o.id=#{?};
</select>
2.3、测试
//测试
@test
public void test2 throws Exception {
SqlSession session = ssf.openSession();
//通过session获取代理
OrdresMapper ordresMapper = session.getMapper(OrdresMapper.class);
Orders orders = ordersMapper.findOrdersBylMap(5);
Sysout.out.prinln(orders );
session.close;
小结:
resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用resultType。
resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的对象属性中。
resultMap可以实现延迟加载,resultType无法实现延迟加载。
4. 一对多
需求:
根据定单ID查找定单信息、用户信息和定单明细信息
4.1、在orders中添加定单信息
//用户信息
private User user;
//订单信息
private List<Orderdetail> orderdetails;
4.2、编写接口
public Orders findOrdersByRslMap(int ordersId);
4.3、编写xml配置文件
<resultMap type="orders" id="ordersResulMap">
<result colum="id" property="id"/>
<result colum="number" property="number"/>
<result colum="createtime" property="createtime"/>
<result colum="note" property="note"/>
<!--关联内部对象-->
<association property="user" javaType="com.entity.User">
<result colum="username" property="username"/>
<result colum="address" property="address"/>
</association>
<!--一对多对应的集合-->
<collection property="orderdetails" ofType="orderdetail">
<id colum="datail_id" property="id"/>
<result colum="items_id" property="itemsId"/>
<result colum="items_num" property="itemsNum"/>
</collection>
</resultMap>
<select id="findOrdersByRslMap" parameterType="int" resultMap="ordersResulMap">
select
orders.id,
orders.user_id,
orders.createtime,
orders.note,
user.username,
user.adderss,
orderdetail.id detail_id,
orderdetail.itemd_id,
orderdetail.itemd_num
from
orders o,user u,orderdetail d
where
o.user_id=u.id
and
o.id=d.orders_id
and
o.id=#{?} ;
</select>
4.4、测试
//测试
@test
public void test2 throws Exception {
SqlSession session = ssf.openSession();
//通过session获取代理
OrdresMapper ordresMapper = session.getMapper(OrdresMapper.class);
Orders orders = ordersMapper.findOrdersByRslMap(7);
Sysout.out.prinln(orders );
session.close;
- 多对多
需求
查询用户信息及用户购买的商品信息,要求将关联信息映射到主pojo的pojo属性中
6.1、User/Orders/Orderdetail.java
//在user实体中加入订单列表
private List<Orders> ordersList;
//在orsers实体中加入订单明细
private List<Orderdetail> orderdetailList;
//在Orderdetail实体中加入商品信息
priv Items items;
6.2、编写接口
public Lise<User> findUserAndItemRslMap();
6.3、编写mapper.xml文件
<mapper namespace="com.mapper.UserMapper">
<resultMap id="userAndItemRslMap" type="user">
<id column="id" property="id"/>
<result columu="username" property="username"/>
<result columu="address" property="address"/>
<!--collection中嵌套collection 嵌套association -->
<!--1.user的ordersList属性-->
<collection property="ordersList" ofType="orders">
<id column="orders_id" property="id"/>
<result columu="numder" property="numder"/>
<result columu="createtime" property="createtime"/>
<result columu="note" property="note"/>
<!--2.orders的orderdetails属性-->
<collection property="orderdetails" ofType="orderdetails">
<id column="details_id" property="id"/>
<result columu="items_id" property="itemsId"/>
<result columu="items_num" property="itemsNum"/>
<!--3.orderdetails的items的属性-->
<association property="items" javaType="com.Items">
<id column="items_id" property="id"/>
<result columu="items_name" property="name"/>
<result columu="items_detail" property="detail"/>
</association>
</cillection>
</collection >
</resultMap>
<select id="findUserAndItemRslMap" resultMap="userAndItemRslMap">
select
user.id,
user.username,
user,adderss,
orders.id orders_id,
orders.user_id,
orders.number,
orders.createtime,
orders.note,
orderdetail.id detail_id,
orderdetail.items_id,
orderdetail.items_num,
items.name items_name,
items.detail items_detail
from
user,orders,orderdetail,items
where
user.id=oedres.user_id
and
orders.id=orderdetail.orders_id
and
orderdetail.items_id=items.id
</select>
</mapper>
6.4、测试
@test
public void test4() throws Exception{
SqlSession session = ssf.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
List<User> userList=userMapper.findUserAndItemRslMap();
System.out.println(userList.size());
for(User user:userList){
System.out.println("用户名:"+user.getUsername()+"-地址"+user.getAddress());
for(Orders orders:getOrdersList()){
System.out.println("定单Id:"+orders.getId());
System.out.println("定单Number:"+orders.getNumber());
System.out.println("定单时间:"+orders.getCreatetiem);
for(Orderdetail detail:order.getOrderdetails()){
System.out.println();
System.out.print("商品数量:"+detail.getItemsNum());
System.out.print("商品名称:"+detail.getItems().getName);
System.out.print("商品详情:"+detail.getItems().getDetail());
}
System.out.println();
System.out.println("-------------------------");
}
}
session.close;
}
于有错误请指点!!!