轻量级的持久层的开源框架,对JDBC进行了完美的封装,是一个不完全的ORM映射框架
MyBatis 简介
MyBatis 作用
MyBatis 就是对数据库中的数据进行操作的一个框架,充当了项目中的数据访问层的角色
MyBatis的执行流程
基本步骤
创建一个java项目,导入mybatis的相关jar包,数据库驱动包
创建一个和src同目录的资源文件夹(config)
在config文件夹下创建一个全局配置文件 SqlMapConfig.xml
注意:每一个xml文件都需要添加自己特有的约束文件
<?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>
<!-- 临时采用jdbc提供的数据源连接池-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC" />
<!-- 数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/girls?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="uesrroot" />
</dataSource>
</environment>
</environments>
<!-- 加载mapper映射文件到全局配置文件
mappers:可以加载多个mapper文件
mapper:具体哪一个mapper文件
resource:指定mapper文件的针对全局文件的相对路径
-->
<mappers>
<mapper resource="AdminMapper.xml"/>
</mappers>
</configuration>
在config下创建一个mybatis配置文件
实现对sql语句的管理以及sql语句中查询语句和查询结果的映射关系配置
标签总结:
- mapper标签:是映射文件的根标签,一个映射文件只能有一个mapper标签,有开始标签和结束标签
- namesapace:命名空间,主要是对sql语句进行隔离,方便管理。但是如果使用mapper动态代理开发,namespace就有特殊的含义了,是xml的全路径名
- select:用于查询的标签
- id:当前sql语句的唯一标识,相当于给sql语句起名字
- resultType:sql语句查询出单条结果要映射的对象类型,类型必须是全路径名,包名+类名;在xml文件中设置别名之后,可以用首字母小写的别名
- " #{} ”:相当于jdbc中的?,{}中传递的参数就是sql条件对应的参数名
- " ${} ”:进行值的拼接,如果传递的值是基本值类型,那么{}中必须是value,相当于把条件直接拼接到sql语句中,容易引起sql注入的问题
- keyProperty:查询到的自增主键映射到输入参数对象的哪个属性上
- order:执行新增语句之后,再去执行当前查询自增主键的函数
- resultType:当前查询自增id返回的类型
<mapper namespace="test">
...
mapper>
查询所有的用户信息
<select id="findadminlist" resultType="com.pojo.Admin">
select * from admin
select>
根据id 查询
<select id="findadminbyid" parameterType="int" resultType="com.pojo.Admin">
select * from admin where id = #{id}
select>
根据名字模糊查询
<select id="findadminbyname" parameterType="String" resultType="com.pojo.Admin">
select * from where username like #{name}
select * from where username like '%${value}%'
select>
根据id删除
<delete id="deleteadminbyid" parameterType="java.lang.Integer">
delete from admin where id = #{id}
delete>
新增
<insert id="inseradmin" parameterType="com.pojo.Admin">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
selectKey>
insert into admin values(#{id},#{username},#{password})
insert>
通过mysql的UUID函数,自行维护主键
使用UUID维护主键注意事项:
- 主键在数据库中的类型是varchar类型,并且长度大于32
- 通过selectKey标签指定,获取UUID的函数
根据id修改
<update id="updateadmin" parameterType="com.pojo.Admin">
update admin set username=#{username},password=#{password} where id=#{id}
update>
总结
1.<select id="" parameterType="" resultType="">
select * from where username like #{name}
select * from where username like '%${value}%'
select>
select:用于查询的标签
id:当前sql语句的唯一标识,相当于给sql语句起名字
parameterType:sql传递的条件的值映射的数据类型,输入映射的类型
resultType:sql语句查询出单条结果要映射的对象类型,类型必须是全路径名,包名+类名,输出结果映
射的类型
#{}:相当于jdbc中的?,{}中传递的参数就是sql条件对应的参数名,大括号中参数任意
${}:相当于把条件直接拼接到sql语句中,容易引起sql注入的问题,{}中参数必须是value
2.增删改不需要指定结果类型映射(resultType)
3.新增
- 通过mysql的一个函数,得到自增主键的值
keyProperty:查询到的自增主键映射到输入参数对象的哪个属性上
order:执行新增语句之后,再去执行当前查询自增主键的函数
resultType:当前查询自增id返回的类型
<insert id="inseradmin" parameterType="com.pojo.Admin">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
selectKey>
insert into admin values(#{id},#{username},#{password})
insert>
- 通过mysql的UUID函数,自行维护主键
使用UUID维护主键注意事项:
1.主键在数据库中的类型是varchar类型,并且长度大于32
2.通过selectKey标签指定,获取UUID的函数
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
select UUID()
selectKey>
...
- 获取oracle序列,实现主键的获取
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Integer">
select 序列名.nexval()
selectKey>
...
创建一个测试类
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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.apache.log4j.lf5.util.Resource;
import org.junit.Test;
import com.pojo.Admin;
public class AdminTest {
// 查询所有用户的信息
// 通过添加单元测试注解的方法来进行单元测试
@Test
public void findadminbyid() throws IOException {
// 1.通过mubatisResources对象的getResourceAsStream("全局配置文件")方法来加载mybatis全局配置文件,创建一个流对象
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2.创建一个SqlSessionFactory对象,需要传递一个加载了全局配置文件的流对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.通过SqlSessionFactory工厂对象,创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.通过sqlSession对象,实现增删改查的操作
List<Admin> admininfo = sqlSession.selectList("findadminbyid");
// 5.打印结果
System.out.println(admininfo);
// 6.关闭资源
sqlSession.close();
}
@Test
public void findadminbyid() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Admin admin = sqlSession.selectOne("test.findadminbyid", 6);
System.out.println(admin);
sqlSession.close();
}
@Test
public void findadminbyname() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Admin> admins = sqlSession.selectList("test.findadminbyname");
System.out.println(admins);
sqlSession.close();
}
@Test
public void deleteadminbyid() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int row = sqlSession.delete("test.deleteadminbyid");
System.out.println(row);
//调用jdbc提供的事务,实现提交操作,使程序中的数据和数据库中的数据保持一致性
sqlSession.commit();
sqlSession.close();
}
@Test
public void inseradmin() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Admin admin = new Admin();
admin.setPassword("9372");
admin.setUsername("小李子");
int row = sqlSession.insert("test.inseradmin", admin);
System.out.println(row);
//调用jdbc提供的事务,实现提交操作,使程序中的数据和数据库中的数据保持一致性
sqlSession.commit();
//查看自增主键的值,意义在于涉及到级联新增(新增部门级联新增一个部门的员工)时,
//System.out.println(admin.getId()); 直接通过查看对象的id值,是获取不到自增主键的
sqlSession.close();
}
@Test
public void updateadmin() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Admin admin = new Admin();
admin.setId(12);
admin.setPassword("9372");
admin.setUsername("小李子");
int row = sqlSession.update("test.updateadmin", admin);
System.out.println(row);
//调用jdbc提供的事务,实现提交操作,使程序中的数据和数据库中的数据保持一致性
sqlSession.commit();
sqlSession.close();
}
}
Dao
层DaoImpl
实现类mapper代理开发 程序员只需要关注接口开发 不需要对实现类进行关注 由mybaits为我们完成具体的数据实现
mapper代理开发需要遵循的开发规范
UserMapper.java
package com.mapper;
import java.util.List;
import com.pojo.User;
public interface UserMapper {
//根据id查询
public User finduserbyid (int id);
//根据姓名模糊查询
public List<User> finduserbyname (String username);
//新增
public boolean insertuser (User user);
}
UserMapper.xml
<mapper namespace="com.mapper.UserMapper">
<select id="finduserbyid" parameterType="int" resultType="user">
select * from user where id = #{id}
select>
<select id="finduserbyname" parameterType="String" resultType="user">
select * from user where username like '%${value}%'
select>
<insert id="insertuser" parameterType="user">
insert into user (username,password) values (#{username},#{password})
insert>
<update id="updateuser" parameterType="user">
update user
update>
mapper>
再建一个test类
package com.test;
import java.io.IOException;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mapper.UserMapper;
import com.pojo.User;
import com.until.SqlSessionFactorySingleton;
public class UserMapperTest {
//根据id查询
@Test
public void usermappertest() throws IOException{
//1.得到sqlsession对象
SqlSession sqlsession = SqlSessionFactorySingleton.getinstance().openSession();
//2.通过sqlsession对象获取mapper接口
UserMapper usermapper = sqlsession.getMapper(UserMapper.class);
//3.调用接口中对数据库进行操作的方法
User user = usermapper.finduserbyid(2);
System.out.println(user);
}
//姓名模糊查询
@Test
public void usermappertest2() throws IOException{
//1.得到sqlsession对象
SqlSession sqlsession = SqlSessionFactorySingleton.getinstance().openSession();
//2.通过sqlsession对象获取mapper接口
UserMapper usermapper = sqlsession.getMapper(UserMapper.class);
//3.调用接口中对数据库进行操作的方法
List<User> list = usermapper.finduserbyname("N");
System.out.println(list);
}
//新增
@Test
public void usermappertest3() throws IOException{
//1.得到sqlsession对象
SqlSession sqlsession = SqlSessionFactorySingleton.getinstance().openSession();
//2.通过sqlsession对象获取mapper接口
UserMapper usermapper = sqlsession.getMapper(UserMapper.class);
//3.调用接口中对数据库进行操作的方法
User user = new User();
user.setUsername("Pass");
user.setPassword("1234");
boolean flag = usermapper.insertuser(user);
sqlsession.commit();
if(flag) {
System.out.println("新增成功");
}else {
System.out.println("失败");
}
}
}
ResultType
作为输出结果映射类型
sql查询的字段名必须和映射对象的属性名完全一致,这样才能够实现数据的绑定
一般在单表查询时使用
ResultMap
(高级映射)
针对别名映射
多表连接查询时,指定映射类型
可以手动绑定查询的字段和映射的结果
查询的字段手动绑定到映射结果对象类型的步骤:
首先定义一个
标签,指定映射结果的对象类型
把查询的字段手动绑定到映射结果对象类型的属性上
<resultMap type="" id="">
<id colum="数据库中查出的字段名" property="映射对象的属性名"/> -- 实现主键的映射
<result colum="" property=""/> -- 实现其他普通字段的映射
resultMap>
type:当前映射的结果对象类型,你查出的结果要映射到哪个对象类型上
id:当前resultMap的唯一标识
为了完成对sql语句的一个灵活组装和拼接,实现sql语句根据不同条件查询不同结果的操作。(想查什么就查什么)
主要实现sql条件的一个动态拼接和封装
where
标签:包含多个条件,会自动去除第一个条件的and
mapper.xml
<select id="querybydynamic" parameterType="user" resultType="user">
select * from user
<where>
<if test="username != null and username != ''">
and username like '%${username}%'
if>
<if test="password != null and password != ''">
and password = #{password}
if>
where>
select>
sql片段
重复的动态条件代码抽离出来,形成一个动态的sql片段
<select id="" parameterType="" resultType="">
select count(*) from user
<where>
<include refid="dynamicquery">include>
where>
select>
建立一个拓展类,包含两个实体类的所有属性
查询语句尽量不写 select * from …
,写具体的查询的字段名:
select orders.id,user_id,number,createtime,note,name,phone,address from orders,user_order where orders.user_id = user_order.id
<select id="findorderanduser" resultType=" ">
select orders.id,user_id,number,createtime,note,name,phone,address
from orders,user_order
where orders.user_id = user_order.id
select>
一对一
<resultMap type="com.pojo.Orders" id="orderanduserresultmap">
<id column="id" property="orders_id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<association property="user" javaType="com.pojo.User">
<result column="phone" property="phone"/>
<result column="address" property="address"/>
<result column="name" property="name"/>
association>
resultMap>
<select id="findorderanduser" resultMap="orderanduserresultmap">
select orders.id,user_id,number,createtime,note,name,phone,address
from orders,user_order
where orders.user_id = user_order.id
select>
包装类
public class Orders {
private int orders_id;
private int user_id;
private String number;
private Date createtime;
private String note;
//包装类
private User user;
测试类
//多表查询,查询订单以及所属的信息
@Test
public void orderstest1() throws IOException{
//1.得到sqlsession对象
SqlSession sqlsession = SqlSessionFactorySingleton.getinstance().openSession();
//2.通过sqlsession对象获取mapper接口
OrdersMapper ordermapper = sqlsession.getMapper(OrdersMapper.class);
//3.调用接口中对数据库进行操作的方法
List list = ordermapper.findorderanduser();
System.out.println(list);
sqlsession.close();
}
一对多
需求:查询用户级联查询用户订单信息
collection:把查询的订单信息,映射到包装类中的集合属性上
ofType:指定集合属性的映射类型
Mapper.xml
<resultMap type="com.pojo.User" id="userandorderresultmap">
<id column="id" property="user_id"/>
<result column="phone" property="phone"/>
<result column="address" property="address"/>
<result column="name" property="name"/>
<collection property="orders" ofType="com.pojo.Orders">
<id column="orders.id" property="orders_id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
collection>
resultMap>
测试类
//查询用户级联查询用户的订单信息
@Test
public void orderstest2() throws IOException{
//1.得到sqlsession对象
SqlSession sqlsession = SqlSessionFactorySingleton.getinstance().openSession();
//2.通过sqlsession对象获取mapper接口
OrdersMapper ordermapper = sqlsession.getMapper(OrdersMapper.class);
//3.调用接口中对数据库进行操作的方法
List<User> list = ordermapper.finduserandorders();
System.out.println(list);
sqlsession.close();
}
多对多
需求:查询用户,关联查询用户所下的订单,以及订单明细,和商品信息
注意事项:
没有关系的两张表不能使用包装类
用户表 — 订单表,一对多
订单表 — 订单明细表,一对多
订单明细 — 商品,一对一
用户表里可以建立订单类,一对多,list
订单表里建立订单明细,一对多,list
订单明细表里建立商品明细,一对一,items
resultMap
注意约束文件中规定的 association,collection的顺序
<mapper namespace="com.mapper.UserMapper">
<resultMap id="userandordersandorderdetailanditemsresultMap" type="com.pojo.User">
<id column="user_id" property="user_id">id>
<result column="user_name" property="name">result>
<result column="phone" property="phone">result>
<result column="address" property="address">result>
<collection property="orders" ofType="com.pojo.Orders">
<id column="orders_id" property="orders_id">id>
<result column="number" property="number">result>
<result column="orders_createtime" property="createtime">result>
<result column="note" property="note">result>
<collection property="orderdetails" ofType="com.pojo.orderdetail">
<id column="items_id" property="items_id">id>
<result column="items_num" property="items_num">result>
<association property="items" javaType="com.pojo.items">
<result column="items_name" property="name">result>
<result column="price" property="price">result>
<result column="detail" property="detail">result>
<result column="items_createtime" property="createtime">result>
association>
collection>
collection>
<select id="finduserbymany" resultMap="userandordersandorderdetailanditemsresultMap">
SELECT
user_order.id user_id,
user_order.`name` user_name,
phone,
address,
orders.id orders_id,
orders.number,
orders.createtime orders_createtime,
orders.note,
orderdetail.items_id,
orderdetail.items_num,
items.`name` items_name,
items.price,
items.detail,
items.createtime items_createtime
FROM
user_order,
orders,
orderdetail,
items
WHERE
user_order.id = orders.user_id AND
orders.id = orderdetail.orders_id AND
orderdetail.items_id = items.id
select>
mapper>
在多表连接查询时先查询一张表的信息,如果用到了关联表的信息时,这个时候再去查询关联表
当前这样的实现方式可以提高查询效率
需求:查询订单关联查询用户信息实现延迟加载
实现步骤:
先创建查询订单的sql语句(查订单单表)
<select id="findorderbylazy" resultMap="findorderbylazy">
select * from orders
select>
再创建根据订单表中的用户id查询订单关联的用户信息的sql
<select id="finduserbylazy" resultType="com.pojo.User" parameterType="int">
select
user_order.id user_id,
user_order.`name`,
phone,
address
from
user_order
where
id = #{id}
select>
给查询订单的sql标签中创建resultmap结果映射通过association或者collection标签设置延迟加载
<resultMap type="orders" id="findorderbylazy">
<id column="id" property="orders_id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<association property="user" javaType="com.pojo.User" select="finduserbylazy" column="user_id">association>
resultMap>
在mybatis全局配置文件中通过setting标签设置开启延迟加载的命令
<settings>
< !-- 开启延迟加载的配置 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
settings>
where 数据库中的字段名 = #{实体类中的属性名} ,必须对应起来
查询的数据库的结果的字段名必须和实体类中的属性名相对应
UserMapper.xml
<select id="finduserbyid" parameterType="int" resultType="user">
select user_order.id user_id,user_order.name,user_order.phone,user_order.address from user_order where id = #{user_id}
select>
<update id="updateuser" parameterType="user">
update user_order set name=#{name},phone=#{phone},address=#{address} where id=#{user_id}
update>
UserMapperTest.java
//一级缓存测试
@Test
public void usertest3() throws IOException {
SqlSession sqlSession = SqlSessionFactorySingleton.getinstance().openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//第一次查询
User user = userMapper.finduserbyid(2);
System.out.println(user);
//第一次查询之后更改
user.setName("Pack");
userMapper.updateuser(user);
sqlSession.commit();;
//第二次查询
User user2 = userMapper.finduserbyid(2);
System.out.println(user2);
sqlSession.close();
}
mapper的缓存
SqlMapConfig.xml
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
settings>
UserMapper.xml
<cache>cache>
pojo实现序列化接口
public class User implements Serializable{
private int user_id;
private String name;
private String phone;
private String address;
UserMapperTest.java
//二级缓存的测试
//(mapper缓存)
@Test
public void usertest4() throws IOException {
SqlSession sqlSession1 = SqlSessionFactorySingleton.getinstance().openSession();
SqlSession sqlSession2 = SqlSessionFactorySingleton.getinstance().openSession();
SqlSession sqlSession3 = SqlSessionFactorySingleton.getinstance().openSession();
SqlSession sqlSession4 = SqlSessionFactorySingleton.getinstance().openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper3 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper4 = sqlSession1.getMapper(UserMapper.class);
//第一次查询
User user = userMapper1.finduserbyid(2);
System.out.println(user);
sqlSession1.close();
//第一次查询之后更改
user.setName("Pack");
userMapper2.updateuser(user);
sqlSession3.commit();;
//第二次查询
User user2 = userMapper2.finduserbyid(2);
System.out.println(user2);
//sqlSession2.close();
}