Mybatis
Mybatis常用配置
mysql,3306多个参数使用,&
分隔
中文需要添加characterEncoding=UTF8"
参数只有一个时,名字随意
使用like模糊查询时,%不能写在映射文件,只能写在参数里
#{随意} 占位符,常用,运行期间编译成?问号,防止sql注入
${},传参时必须加入单引号,一般不用,直接拼接,没有防注入
适合用列名使用
参数是引用数据类型,参数名是javabean对象的属性名
执行增删改,自动开启事务,需要手动提交事务!!!!!!!!!!!
全局配置
<?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>
<!-- 设置log4j对象,交给spring容器-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 支持多环境开发,默认是id为开发环境-->
<environments default="development">
<!-- id为development的开发环境-->
<environment id="development">
<!-- 配置事务管理器-->
<transactionManager type="JDBC"/>
<!-- 配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
//加上编码,不然容易乱码,多个参数使用转义分隔,&
<property name="url" value="jdbc:mysql://localhost:3306/book_test?characterEncoding=UTF8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--
配置一个xml文件,存储的SQL语句
resource 配置的是存储SQL语句的文件路径和文件名
一个表一个文件
-->
<mapper resource="User.xml"/>
</mappers>
</configuration>
映射sql文件
<?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="com.stu.mybatis.User">
<!-- select标签,配置查询语句标签,语义化标签-->
<!--
当前文件的id查询语句的唯一性
resultType是返回类型
-->
<select id="listUser" resultType="com.stu.User">
<!-- select 标签体文本,就是SQL语句-->
select user_id userId,user_name userName,password,email from t_user
</select>
<!--
增加用户,parameter表示参数类型
多个参数,如果是引用类型,字段必须是引用的字段名
-->
<insert id="insertUser" parameterType="com.stu.User">
insert into t_user values (#{userId},#{userName},#{password},#{email});
</insert>
<!--
删除用户
参数类型是int,传入id删除某个用户
-->
<delete id="deleteUser" parameterType="int">
delete from t_user where user_id = #{id};
</delete>
<!-- 修改用户,多个参数,如果是引用类型,字段必须是引用的字段名-->
<update id="updateUser" parameterType="com.stu.User">
update t_user set user_name = #{userName} ,password = #{password},email = #{email} where user_id = #{userId};
</update>
<!-- 查询单个用户,返回类型是user类-->
<select id="selectOneUser" parameterType="int" resultType="com.stu.User">
select user_id userId,user_name userName,password,email from t_user where user_id = #{id}
</select>
<!-- 插入后返回主键,有自增长,key的属性必须是类字段-->
<insert id="insertGetKey" keyProperty="userId" parameterType="com.stu.User" useGeneratedKeys="true">
insert into t_user values (#{userId},#{userName},#{password},#{email});
</insert>
<!-- 插入后返回主键,没有自增长
配置子参数,使用select_last_id获得主键,执行顺序在sql语句之后
要存储的属性值,返回值类型
-->
<insert id="insertGetNoKey" parameterType="com.stu.User">
<selectKey order="AFTER" keyProperty="userId" resultType="int">
select last_insert_id()
</selectKey>
insert into t_user values (#{userId},#{userName},#{password},#{email});
</insert>
<!-- 模糊查询-->
<select id="selectByLike" parameterType="String" resultType="com.stu.User">
select user_id userId,user_name userName,password,email from t_user where user_name like #{name}
</select>
</mapper>
配置新增的SQL语句,参数是EmployeeVo对象
Vo对象中,包装Employee对象 Vo Value-Object
<insert id="insertEmployeeVo" parameterType="com.atguigu.pojo.EmployeeVo">
<!-- 参数是一个对象的包装类型, 包装类型的字段名.就行 -->
insert into employee values(#{employee.id},#{employee.name},#{employee.salary})
</insert>
resultType是返回类型不能和resultMap同时使用
使用步骤
//读取配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// 得到sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// 得到session实例,执行已经映射的sql实例
SqlSession session=sqlSessionFactory.openSession();
//传入一个唯一标识
List<User> userList = session.selectList("com.stu.mybatis.User.listUser");
函数接口--动态代理很重要
包名舍去dao,daoImpl,统一叫mapper包
四合一
- sql映射文件的namespace必须和mapper接口的全限定类名保持一致
- mapper接口的接口方法名必须和xml中的sql语句id保持一致
- mapper接口的接口方法形参类型必须和sql语句的输入参数类型保持一致
- mapper接口的接口方法返回类型必须和sql语句的resultType保持一致
<!-- 和函数式接口绑定 ,使用接口全名 -->
<mapper namespace="com.stu.mapper.UserMapper">
</mapper>
使用类的别名-不区分大小写
<typeAliases>
<!-- 为pojo对象定义别名,不区分大小写-->
<!-- <typeAlias type="com.stu.User" alias="user"></typeAlias>-->
<!-- 使用包扫描,扫描stu包下的所有类-->
<!--
typeAliases里面就是别名配置
1. 每一个typeAlias标签就表示配置一个别名
type属性:要进行别名配置的类
alias属性:取的别名
2. 因为所有的POJO类基本上都是放在同一个包中,所以我们可以采用包扫描进行别名配置
用package标签进行包扫描,别名就是该类的类名(不区分大小写)
我们一般采用第二种(也就是包扫描的方式进行别名配置)
-->
<package name="com.stu"/>
</typeAliases>
使用框架的包扫描,子标签package,名字自动重命名,就是类名
<!-- 使用别名即可-->
<!-- 查询单个用户,返回类型是user类,改为接口方法-->
<select id="queryUserById" parameterType="int" resultType="user">
select user_id userId,user_name userName,password,email from t_user where user_id = #{id}
</select>
全局配置sql配置文件
多个sql配置文件,一个个去映射会显得很臃肿,所以把文件放到同名mapper路径下
在resource里配置同名包路径com/stu/mapper把sql配置文件放在这里
注意,创建文件夹不能直接写(.) 写(/)
配置文件名字和接口名字一致
创建和包名路径一致的文件
com/stu/mapper 创建文件时,会自动变成点
UserMapper.xml放在这
<mappers>
<!--
配置一个xml文件,存储的SQL语句
resource 配置的是存储SQL语句的文件路径和文件名
一个表一个文件
-->
<!-- <mapper resource="UserTwo.xml"/>-->
//统一使用包扫描
<package name="com.stu.mapper"/>
</mappers>
增删改可以定义接口方法的时候定义为boolean
int返回受影响的行数,boolean返回大于0行就是true
获得sqlSession的时候,可以填入true,opensession(true),可以自动提交事务
配置使自动映射驼峰命名规则
<settings>
<!-- 设置log4j对象,交给spring容器-->
<setting name="logImpl" value="LOG4J"/>
<!-- 具体配置 -->
<!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
<!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
<!-- 规则要求数据库表字段命名方式:单词_单词 -->
<!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
手动映射
当名字不一样,且也不符合自动映射的规则时
使用手动映射
- resultMap标签可以实现手动映射
- id属性:自定义的唯一值
- type属性:手动映射后的查询结果集类型
- id子标签:映射主键使用
- property属性:对应pojo对象的字段名
- column属性:对应数据表的列名
- result子标签:映射数据表其他字段
- property属性:对应pojo对象的字段名
- column属性:对应数据表的列名
- select标签属性resultMap,关联resultMap标签的id值
- id可以取名为select的id加resultMap,见名之意
- resultType是返回类型不能和resultMap同时使用---------------------
<!--
手动映射:通过resultMap标签配置映射规则
1. id属性:表示这个手动映射规则的唯一表示
2. type属性: 表示这个手动映射规则是将结果集映射给哪个类的对象,就是JavaBean类的全限定名
resultMap标签中的子标签就是一一指定映射规则:
1. id标签:指定主键的映射规则
2. result标签:指定非主键的映射规则
id标签和result标签的属性:
1. column:要进行映射的结果集的字段名
2. property:要进行映射的JavaBean的属性名
-->
实现步骤
<resultMap id="EmployeeInfoMap" type="com.atguigu.pojo.EmployeeInfo">
<id column="emp_id" property="id"/>
<result column="emp_name" property="name"/>
<result column="emp_salary" property="salary"/>
</resultMap>
<!--
在select标签中通过resultMap属性来指定使用哪个手动映射规则
-->
<select id="selectEmployeeInfoByEmpId" resultMap="EmployeeInfoMap">
select * from t_emp where emp_id=#{empId}
</select>
返回自增长主键
<!--
插入后返回主键,有自增长,key的属性必须是类字段
useGeneratedKeys属性字面意思就是“使用生成的主键”
keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性
-->
<insert id="insertGetKey" keyProperty="userId" parameterType="user" useGeneratedKeys="true">
insert into t_user values (#{userId},#{userName},#{password},#{email});
</insert>
不支持自增主键的数据库怎么获取主键值
<!-- 插入后返回主键,没有自增长
配置子参数,使用select_last_id获得主键,执行顺序在sql语句之后
要存储的属性值,返回值类型
可以使用 selectKey 子元素:selectKey元素将会首先运行,id 会被设置,然后插入语句会被调用
-->
<insert id="insertGetNoKey" parameterType="user">
<selectKey order="AFTER" keyProperty="userId" resultType="int">
select last_insert_id()
</selectKey>
insert into t_user values (#{userId},#{userName},#{password},#{email});
</insert>
注解
sql注解写在接口方法上
适合简单业务,复杂业务还是得配置文件
//主键查询用户
@Select("select id,username,sex,birthday,address from user where id = #{id}")
User queryUserById(int id);
//查询全部用户
@Select("select id,username,sex,birthday,address from user")
List<User> queryUserByList();
假设sql查询结果没有对应的javabean,可以使用map封装
键是id,值是每一条封装好的对象
@mapKey("要作为key的属性值")
map<Integer User> getUser(String name);
@Insert注解
注解属性value:写入SQL语句
//新增用户数据
@Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")
void saveUser(User user);
@Options注解
实现添加新数据的主键封装
注解属性
- useGeneratedKeys:使用生成的主键,配置为true
- keyProperty:主键封装的pojo对象属性
@SelectKey注解
- 实现添加新数据的主键封装
- 注解属性
- statement:要执行的SQL语句
- before:在添加SQL语句之前还是之后进行,配置为false
- keyProperty:主键封装的pojo对象属性
@Update注解
注解属性value:写入SQL语句
//更新用户
@Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
void updateUser(User user);
@Delete注解
//删除用户
@Delete("delete from user where id = #{id}")
void deleteUser(int id);
假设包下面有个子包,子包也有个同名的类
可以使用注解取别名
@Alias("别名") 注明在类上
一对一的注解开发
@Results注解
配置手动映射,取代resultMap标签
OrdersMapper接口
//根据订单查询用户,一对一查询
@Select(" SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o")
@Results({
//配置主键映射,id默认false,不是主键
@Result(id = true,column = "id",property = "id"),
//配置其他映射关系
@Result(column = "user_id",property = "userId"),
@Result(column = "number",property = "number"),
@Result(column = "createtime",property = "createtime"),
@Result(column = "note",property = "note"),
/*
配置关联查询用户表
property查询的pojo对象哪个属性做为条件查询
这个属性还是个pojo对象
column查询条件的pojo对象的属性
@One注解配置一对一的另一个查询语句
此语句需要对应的接口方法出现
*/
@Result(column = "user_id",property = "user",javaType = User.class,
one = @One(select = "com.atguigu.mapper.UserMapper.queryUserByUserId",fetchType = FetchType.LAZY))
})
List<Orders> queryOrdersUser();
UserMapper接口
@Select("select id,username,sex,birthday,address from user where id=#{user_Id}")
User queryUserByUserId(Integer id);
一对多注解开发
UserMapper接口
//用户查询订单,一对多
@Select("select id,username,sex,birthday,address from user")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property ="username" ),
@Result(column = "sex",property ="sex" ),
@Result(column = "birthday",property ="birthday" ),
@Result(column = "address",property ="address" ),
@Result(column = "id",property = "ordersList",javaType = List.class,
many = @Many(select = "com.atguigu.mapper.OrdersMapper.queryOrdersByUserId",fetchType = FetchType.LAZY))
})
List<User> queryUserOrders();
OrdersMapper接口
//用户查询订单,一对多
@Select("select number,createtime from orders where user_id = #{user_id}" )
List<Orders> queryOrdersByUserId(Integer user_id);
表查询-------常用
sql配置文件参数parameterType可以省略
1对1查询,一个订单对应一个用户
association(联合)标签,实现手动映射
- propery属性:封装的pojo对象
- javaType属性:封装的pojo对象类型
- autoMapping属性,如果值对象也符合驼峰命名,则开启自动映射
即使全局配置了驼峰自动映射,但是属性 autoMapping="true"还是得开启才有数据
胡老师
<!-- 一对一查询,通过订单查询用户2-->
<select id="queryOrdersUser2" resultMap="queryOrdersUserResultMap2" >
select o.order_id,o.user_id,o.number,u.address,u.user_name from orders o left join user u
on o.user_id=u.user_id
</select>
<!-- 手动设置映射2-->
<resultMap id="queryOrdersUserResultMap2" type="orders" autoMapping="true">
<!-- 手动映射,配置User对象,property字段名,javaType封装类型-->
<association property="user" javaType="user" autoMapping="true">
<!-- 手动映射,配置User对象,property字段名,javaType封装类型-->
</association>
</resultMap>
石松老师
使用association标签
<!-- 一对一查询,通过订单查询用户-->
<select id="queryOrdersUser" resultMap="queryOrdersUserResultMap" >
select o.order_id,o.user_id,o.number,u.address,u.user_name from orders o left join user u
on o.user_id=u.user_id
</select>
<!-- 手动设置映射-->
<resultMap id="queryOrdersUserResultMap" type="orders">
<id column="order_id" property="OrderId"></id>
<result column="user_id" property="userId"></result>
<result column="number" property="number"></result>
<result column="createtime" property="createtime"></result>
<!-- 手动映射,配置User对象,property字段名,javaType封装类型-->
<association property="user" javaType="user">
<id column="user_id" property="userId"></id>
<result column="address" property="address"></result>
<result column="user_name" property="userName"></result>
</association>
</resultMap>
尚硅谷方式
<!-- 手动设置映射-->
<resultMap id="queryOrdersUserResultMap" type="orders">
<id column="order_id" property="OrderId"></id>
<result column="user_id" property="userId"></result>
<result column="number" property="number"></result>
<result column="createtime" property="createtime"></result>
<result column="user_name" property="user.userName">
</result>
...使用值属性的.可以调用值
</resultMap>
一对多表查询
1对多查询,一个用户对应多个订单,不要使用自动映射
public interface UserMapper {
//根据用户名和性别查询用户
List<User> queryUserByWhere(User user);
//订单一对一查询用户
List<Orders> queryOrdersUser();
//订单一对一查询用户2
List<Orders> queryOrdersUser2();
//查询用户的多个订单
List<User> queryUserOrders();
//根据用户id查询订单
User queryOrdersById(Integer id);
}
xml配置文件
<!-- 根据用户id查询订单-->
<select id="queryOrdersById" resultMap="queryOrdersByIdResultMap">
select * from user u,orders o where
u.user_id = o.user_id and u.user_id = #{id}
</select>
<resultMap id="queryOrdersByIdResultMap" type="user" >
<!-- 多个结果不能自动映射在一个pojo,需要手动映射,返回结果是一个用户,自定映射的多条数据装不了-->
<id column="user_id" property="userId"></id>
<!-- 多个订单映射结果有多个,而返回类型刚好是list,所以可以使用自动映射-->
<collection property="ordersList" ofType="orders" autoMapping="true"></collection>
</resultMap>
注意
一对多当使用自动映射的时候,会报错,因为自动映射会返回三条数据,而用户只有一个
手动映射则不会报错
Expected one result (or null) to be returned by selectOne(), but found: 2
多对多查询
public class Role {
private Integer roleId;
private String roleName;
private List<User> userList;
}
xml配置文件
<mapper namespace="com.stu.mapper.RoleMapper">
<!-- 多对多查询-->
<select id="queryRoleUser" resultMap="queryRoleUserResultMap">
select * from role r left join user_role ur on r.`role_id` = ur.`role_id`
left join user u on u.`user_id` = ur.`user_id`
</select>
<resultMap id="queryRoleUserResultMap" type="role">
<id column="role_id" property="roleId"></id>
<result column="role_name" property="roleName"></result>
<collection property="userList" ofType="user" autoMapping="true"></collection>
</resultMap>
</mapper>
输出结果
List<Role> roles = mapper.queryRoleUser();
if (roles.size() > 0 && roles != null){
for (Role role : roles) {
System.out.println(role);
}
}
延迟加载
延迟加载就是对于实体类关联的属性到需要使用时才查询
查询到user的时候,不一定会使用Order的List集合数据。如果Order的集合数据始终没有使用,那么这部分数据占用的内存就浪费了
为了实现延迟加载,对Customer和Order的查询必须分开,分成两步来做
局部延迟加载
<association property="user" column="user_id"
// 延迟加载
fetchType="lazy" select="com.stu.mapper.UserMapper.selectUserById"></association>
全局延迟加载
<settings>
<!-- 开启延迟加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
分步查询
一对一分步查询
接口方法
//传入一个订单查询用户分步式和懒加载
Order selectOrderUser(Integer id);
orderMapper
<!-- 传入一个订单查询用户分步式和懒加载-->
<select id="selectOrderUser" resultMap="selectOrderUserResultMap">
select order_id,user_id,number,createtime from orders where order_id = #{id}
</select>
<resultMap id="selectOrderUserResultMap" type="Order" >
<id column="order_id" property="OrderId"></id>
<result column="user_id" property="userId"></result>
<!-- 开始和用户表交互,开启懒加载-->
<association property="user" column="user_id" fetchType="lazy" select="com.stu.mapper.UserMapper.selectUserById"></association>
</resultMap>
userMapper
<!-- 传入一个用户id查询用户信息-->
<select id="selectUserById" resultMap="selectUserByIdResultMap">
select user_id,user_name,sex,birthday,address from user where user_id = #{id}
</select>
<resultMap id="selectUserByIdResultMap" type="user">
<id column="user_id" property="userId"></id>
<result column="user_name" property="userName"></result>
</resultMap>
一对多查询
//根据用户id查询订单,一对多
User selectOrderById(Integer id);
userMapper
<!-- 根据用户id查询订单,一对多-->
<select id="selectOrderById" resultMap="selectOrderByIdResultMap">
select user_id,user_name,sex,birthday,address from user where user_id = #{id}
</select>
<resultMap id="selectOrderByIdResultMap" type="user">
<id column="user_id" property="userId"></id>
<result column="user_name" property="userName"></result>
<collection property="orderList" column="user_id" ofType="order" fetchType="lazy" select="com.stu.mapper.OrderMapper.selectOrderByUserId"></collection>
</resultMap>
ordermapper
<!-- 根据用户名id返回多个订单-->
<select id="selectOrderByUserId" resultMap="selectOrderByUserIdResultMap" >
select order_id,user_id,number,createtime from orders where user_id = #{id}
</select>
<resultMap id="selectOrderByUserIdResultMap" type="Order" >
<id column="order_id" property="OrderId"></id>
<result column="user_id" property="userId"></result>
</resultMap>
//根据用户名id返回多个订单
List<Order> selectOrderByUserId(Integer id);
mybatis不能使用in查询
自动遍历查询,使用=即可