Mybatis架构
sqlMapConfig.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>
<properties resource="jdbc.properties"/>
<!-- 别名 包以其子包下所有类 头字母大小都行-->
<typeAliases>
<!-- <typeAlias type="com.itheima.mybatis.pojo.User" alias="User"/> -->
<package name="com.itheima.mybatis.pojo"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- Mapper的位置 Mapper.xml 写Sql语句的文件的位置 -->
<mappers>
<!-- <mapper resource="sqlmap/User.xml" class="" url=""/> -->
<!-- <mapper resource="sqlmap/User.xml" class="" url=""/> -->
<!-- <mapper class="com.itheima.mybatis.mapper.UserMapper" /> -->
<!-- <mapper url="" /> -->
<!-- 此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中 -->
<package name="com.itheima.mybatis.mapper"/>
</mappers>
</configuration>
UserMapper.xml
若User类属性与user表字段名字相同,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">
<!-- 写Sql语句 -->
<mapper namespace="com.itheima.mybatis.mapper.UserMapper">
<!-- 通过ID查询一个用户 -->
<select id="findUserById" parameterType="Integer" resultType="User">
select * from user where id = #{v}
</select>
<!-- //根据用户名称模糊查询用户列表
#{} select * from user where id = ? 占位符 ? == '五' 会自动带上单引号(预编译),括号内填任意值
${} select * from user where username like '%五%' 字符串拼接 不带单引号,括号内只能填value.${value}.不防sql注入
-->
<select id="findUserByUsername" parameterType="String" resultType="com.itheima.mybatis.pojo.User">
select * from user where username like "%"#{haha}"%"
</select>
<!-- 添加用户 -->
<insert id="insertUser" parameterType="com.itheima.mybatis.pojo.User">
<selectKey keyProperty="id" resultType="Integer" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
insert into user (username,birthday,address,sex)
values (#{username},#{birthday},#{address},#{sex})
</insert>
<!-- 更新 -->
<update id="updateUserById" parameterType="com.itheima.mybatis.pojo.User">
update user
set username = #{username},sex = #{sex},birthday = #{birthday},address = #{address}
where id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteUserById" parameterType="Integer">
delete from user
where id = #{vvvvv}
</delete>
</mapper>
?
@Test
public void testMybatis() throws Exception {
//加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行Sql语句
User user = sqlSession.selectOne("test.findUserById", 10);
System.out.println(user);
}
Mapper动态代理开发
? 原始Dao开发需要写Dao接口,DaoImpl实现类,调用session的selectOne/selectList/update/insert/delete方法,实现类中各方法本质上只有语句id和参数不同.而且还要在每个方法中调用SqlSessionFactory的openSession方法.因此会显得很繁琐.
动态代理开发只需要写接口,实现类由Mybatis动态生成
Mapper接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
public interface UserMapper {
//遵循四个原则
//接口 方法名 == UserMapper.xml 中 id 名
//返回值类型 与 Mapper.xml文件中返回值类型要一致
//方法的入参类型 与Mapper.xml中入参的类型要一致
//命名空间 绑定此接口
public User findUserById(Integer id);
}
?
@Test
public void testMapper() throws Exception {
//加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSEssion帮我生成一个实现类 (给接口)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(10);
System.out.println(user);
}
selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
parameterType输入类型
Mybatis使用ognl表达式解析对象字段的值,#{}或${}中的值为pojo属性名称
传递简单类型
String Integer
传递pojo对象
传递pojo包装对象
开发中通过pojo传递查询条件,条件可能是综合的查询条件,不仅包括用户查询条件还包括其他的查询条件,这时可以使用包装对象传递输入参数.
包装对象:pojo类中的一个属性是另外一个pojo
根据用户名模糊查询(QueryVo对象中有一个User属性)
<select id="findUserByQuery" parameterType="QueryVo" resultType="User">
select * from user where username like "%"#{user.username}"%"
</select>
resultType输出类型
输出简单类型
public Integer countUser();
<select id="countUser" resultType="Integer">
select count(*) from user
</select>
输出pojo对象
<select id="findUserByUsername" parameterType="String" resultType="User">
select * from user where username like "%"#{aa}"%"
</select>
输出pojo列表
同上,mybatis会根据返回数量决定返回单个user对象或是list
resultMap
如果sql查询字段名与pojo属性名不一致,可以通过resultMap将字段名和属性名做一个对应关系
public List<Orders> selectOrdersList();
<resultMap type="Orders" id="aaa">
<!-- 若有部分字段名与属性名一致,可省略不写,自动映射 -->
<id 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"/>
</resultMap>
<select id="selectOrdersList" resultMap="aaa">
select id, user_id, number, createtime, note from orders
</select>
?
动态sql
通过Mybatis提供的各种标签方法实现动态拼接sql
需求:根据性别和名字查询用户
if标签
public List<User> selectUserBySexAndUsername(User user);
<!-- 传来的user中sex和username可能为空 -->
<select id="selectUserBySexAndUsername" parameterType="User" resultType="User">
select * from user
where 1 = 1
<if test="sex != null and sex != ' '">
and sex = #{sex}
</if>
<if test="username != null and username != ' '">
and username = #{username}
</if>
</select>
where标签
上面的sql还需要使用where 1=1 这样的语句,很麻烦,可以使用where标签进行改造
public List<User> selectUserBySexAndUsername(User user);
<!-- where标签可以去掉第一个前置and -->
<select id="selectUserBySexAndUsername" parameterType="User" resultType="User">
select * from user
<where>
<if test="sex != null and sex != ' '">
and sex = #{sex}
</if>
<if test="username != null and username != ' '">
and username = #{username}
</if>
</where>
</select>
sql标签
Mybatis中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的
<sql id="selectall">
select * from user
</sql>
<select id="selectUserBySexAndUsername" parameterType="User" resultType="User">
<include refid="selectall"/>
<where>
<if test="sex != null and sex != ' '">
and sex = #{sex}
</if>
<if test="username != null and username != ' '">
and username = #{username}
</if>
</where>
</select>
foreach标签
向sql传递数组或List,Mybatis使用foreach解析
需求:根据多个id查询用户信息
select * from user where id in (1,10,24)
若传递QueryVo包装类.collection填其数组或list的属性名
//public List<User> selectUserByIds(Integer[] ids);
//public List<User> selectUserByIds(List<Integer> ids);
public List<User> selectUserByIds(QueryVo vo);//QueryVo中含有数组或List属性
<select id="selectUserByIds" parameterType="QueryVo" resultType="User">
<include refid="selectall"/>
<where>
id in
<foreach collection="idsList" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
若传递数组.collection=“array”(parameterType可不写??)
public List<User> selectUserByIds(Integer[] ids);
<select id="selectUserByIds" resultType="User">
<include refid="selectall"/>
<where>
id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
若传递集合list.collection=“list”(parameterType可不写??)
public List<User> selectUserByIds(List<Integer> ids);
<select id="selectUserByIds" resultType="User">
<include refid="selectall"/>
<where>
id in
<foreach collection="list" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
关联查询
订单与用户
一对一查询
需求:查询所有订单信息,关联查询下单用户信息
select o.id, o.user_id, o.number, o.createtime, u.username from orders o left join user u on o.user_id = u.id
方法一:使用resultType
新建一个OrderUser类继承Order类,OrderUser类包括了Order类的所有字段,只需要定义用户的信息字段即可
private String username
方法二:使用resultMap
在Order类中加入User属性,user属性中用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息.
//以订单为中心,关联用户
OrderMapper
public List<Orders> selectOrders();
<!-- 关联查询时,即使字段与属性名相同,也需要手动配置映射 -->
<resultMap id="zzz" type="Orders">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<!-- 一对一 -->
<association property="user" javaType="User">
<!-- id:声明主键,表示user_id是关联查询对象的唯一标识-->
<id column="user_id" property="id" />
<result column="username" property="username"/>
</association>
</resultMap>
<select id="selectOrders" resultMap="zzz">
select
o.id,
o.user_id,
o.number,
o.createtime,
u.username
from orders o
left join user u
on o.user_id = u.id
</select>
一对多查询
需求:查询所有用户信息及用户关联的订单信息
select o.id, o.user_id, o.number, o.createtime, u.username from user u left join orders o on o.user_id = u.id
修改pojo类:在User类中加入订单属性
private List<Orders> ordersList;
//以用户为中心,关联订单
public List<User> selectUserList();
<resultMap id="qqq" type="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<!-- 一对多 -->
<collection property="ordersList" ofType="Orders">
<id column="id" property="id"/>
<result column="number" property="number"/>
</collection>
</resultMap>
<select id="selectUserList" resultMap="qqq">
select
o.id,
o.user_id,
o.number,
o.createtime,
u.username
from user u
left join orders o
on o.user_id = u.id
</select>
Mybatis整合Spring
整合思路
1、SqlSessionFactory对象应该放到spring容器中作为单例存在
2、传统dao的开发方式中,应该从spring容器中获得sqlsession对象
3、Mapper代理形式中,应该从spring容器中直接获得mapper的代理对象
4、数据库的连接以及数据库连接池事务管理都交给spring容器来完成
SqlMapConfig.xml
<configuration>
<!-- 设置别名 -->
<typeAliases>
<!-- 2. 指定扫描包,会把包内所有的类都设置别名,别名的名称就是类名,大小写不敏感 -->
<package name="cn.itcast.mybatis.pojo" />
</typeAliases>
</configuration>
applicationContext.xml
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置mybatis核心配置文件 -->
<property name="configLocation" value="classpath:SqlMapConfig.xml" />
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
Dao开发
?
传统Dao的开发方式
原始的DAO开发接口+实现类来完成。
需要dao实现类需要继承SqlsessionDaoSupport类
还需要在spring中为dao注入sqlSessionFactory属性
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
@Override
public User queryUserById(int id) {
// 获取SqlSession
SqlSession sqlSession = super.getSqlSession();
// 使用SqlSession执行操作
User user = sqlSession.selectOne("queryUserById", id);
// 不要关闭sqlSession
return user;
}
@Override
public List<User> queryUserByUsername(String username) {
// 获取SqlSession
SqlSession sqlSession = super.getSqlSession();
// 使用SqlSession执行操作
List<User> list = sqlSession.selectList("queryUserByUsername", username);
// 不要关闭sqlSession
return list;
}
@Override
public void saveUser(User user) {
// 获取SqlSession
SqlSession sqlSession = super.getSqlSession();
// 使用SqlSession执行操作
sqlSession.insert("saveUser", user);
// 不用提交,事务由spring进行管理
// 不要关闭sqlSession
}
}
Mapper动态代理形式开发dao
方式一:配置mapper代理
在applicationContext.xml添加配置
MapperFactoryBean也是属于mybatis-spring整合包
SqlMapConfig.xml中需要配置Mapper文件的包路径(方式二不用)
<!-- Mapper代理的方式开发方式一,配置Mapper代理对象 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 配置Mapper接口 -->
<property name="mapperInterface" value="cn.itcast.mybatis.mapper.UserMapper" />
<!-- 配置sqlSessionFactory -->
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
方式二:扫描包形式配置mapper(建议)
<!-- Mapper代理的方式开发方式二,扫描包方式配置代理
会自动找到容器中的工厂对象,无需手动注入
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置Mapper接口的包路径 -->
<property name="basePackage" value="cn.itcast.mybatis.mapper" />
</bean>
每个mapper代理对象的id就是类名,首字母小写
?
逆向工程
使用官方网站的Mapper自动生成工具mybatis-generator-core-1.3.2来生成pojo类,Mapper映射文件和Mapper接口
注意:
1.逆向工程生成的代码只能做单表查询
2.不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了
3.一张表会生成4个文件