mybatis

一、mybatis架构

1、mybatis介绍

mybatis本是apache的一个开源项目iBatis,2010年这个项目由apache software迁移到了google code,并且改名为mybatis,实质上对ibatis进行一些改进。目前mybatis在github上托管。

mybatis是持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,不需要花费精力去处理其他东西。

mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

2、mybatis架构

SqlMapConfig.xml:

SqlMapConfig.xml中配置的内容顺序如下:

properties(属性)

settings(全局配置参数)

typeAliases(类型别名)

typeHandlers(类型处理器)

objectFactory(对象工厂)

plugins(插件)

environments(环境集合属性对象)

environment(环境子属性对象)

transactionManager(事务管理)

dataSource(数据源)

mappers(映射器)

mybatis的全局配置文件,名称不固定,主要配置的是运行环境(数据源、事物),设置全局参数,设置别名

引入数据源外部文件:

    <!-- 1、属性定义引入外部数据库文件 -->
    <properties resource="db.properties"></properties>

设置全局参数:

<settings></settings>

设置别名:

<!-- 3、设置别名 -->
	<typeAliases>
		<!-- 单个别名的定义
		alias:别名,type:别名映射的类型 你的类的全局路径-->
		<typeAlias type="cn.itcast.mybatis.pojo.User" alias="user"/>
		<typeAlias type="cn.itcast.mybatis.parameterType.UserQueryVo"    alias="userQueryVo"/>
		<!-- 指定包路径,自动扫描包下边的pojo,定义别名,别名默认为类名 -->
		<!-- <package name="cn.itcast.mybatis.pojo"/> -->
	</typeAliases>

配置数据库:(和spring整合后不需要了)

    <environments default="development">
		<environment id="development">
        <!-- 使用jdbc事物管理-->
      	<transactionManager type="JDBC"/>
        <!-- 数据库连接池-->
	      <dataSource type="POOLED">
	        <property name="driver" value="${jdbc.driver}"/>
	        <property name="url" value="${jdbc.url}"/>
	        <property name="username" value="${jdbc.username}"/>
	        <property name="password" value="${jdbc.password}"/>
	      </dataSource>
   	 	</environment>
  	</environments>

引入mapper.xml文件(该文件主要配置的是sql语句):

    <mappers>
  		<mapper resource="mapper/mapper.xml"/>
  	</mappers>

mapper.xml:

mybatis有两种开发dao的方法:(1)原始开发dao方式(dao接口和dao实现都需要编写)(2)mapper代理方式(只需要写dao接口)

原始开发dao方式

mapper.xml文件编写:

<!-- namespace命名空间,为了对sql语句进行隔离,方便管理-->
<mapper namespace="test">
	<!-- 根据用户id查询信息 mapper.xml以statement为单位管理sql语句,封装为mappedStatement对象-->
    <!--id:唯一标识一个statement-->
    <!--parameterType:输入的参数类型-->
    <!--ireturnType:输出的结果类型,单条记录映射的pojo类型-->
	<select id="findUserById" resultType="cn.itcast.mybatis.pojo.User" parameterType="int">
		select * from user where id = #{id}
	</select>
</mapper>

需要编写dao接口和dao的实现类

dao接口:

public interface UserDao {
	public User findUserById(int id) throws Exception;
}

dao实现类:

public class UserDaoImpl implements UserDao{
	//工厂类
	private SqlSessionFactory sqlSessionFactory;
	
	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
		super();
		this.sqlSessionFactory = sqlSessionFactory;
	}
	public User findUserById(int id) throws Exception{
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//根据id查询信息
        // 如果查询结果返回的是多条记录,使用selectOne();
		User user = sqlSession.selectOne("test.findUserById", 1);
		sqlSession.close();
		return user;
	}
}

原始开发dao配置文件创建SqlSessionFactory

    private SqlSessionFactory sqlSessionFactory;
	
	@Before
	public void init() throws IOException{
        // 全局配置文件
		String resource = "SqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建sqlSessionFactory
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

原始开发dao获取测试

    @Test
	public void testFindUserById(){
		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
        User user = userDao.findUserById(1);
        System.out.println(user);
	}

mapper代理方式:

程序员只需要写dao接口,到接口实现对象由mybatis自动生成代理对象。

原始开发dao方式存在问题:

1、dao的实现类中 存在重复代码,整个mybatis操作的过程代码模块重复(先创建sqlSession、调用sqlsession的方法、关闭sqlsession)

2、dao的实现类中存在硬编码,调用sqlsession方法时将statement的id硬编码。

mapper.xml文件和mapper.java文件(每个方法输入参数只能是一个,想输入多个通过扩展pojo)编写:

1、mapper.xml中的namespace指定为mapper接口的权限定名:将mapper.xml和mapper.java进行关联

<!-- namespace命名必须是指定为mapper接口的全限定名 -->
<mapper namespace="cn.itcast.mybatis.dao2.UserMapper">

2、mapper.xml中的statement的id就是mapper.java中的方法名:

3、mapper.xml中的statement的parameterType和mapper.java中方法输入参数类型一致

4、mapper.xml中的statement的resultType和mapper.java中方法返回值类型一致

    <select id="findUserById" resultType="user" parameterType="int">
		select * from user where id = #{id}
    </select>
    public interface UserMapper {
	    public User findUserById(int id) throws Exception;

5、mapper接口中返回单个对象和集合对象

不管查询记录是单条还是多条,在statement中resultType定义一致,都是单条记录映射的pojo类型。

mapper接口方法返回值,如果返回的单个对象,返回值类型是pojo类型,生成的代理对象内部通过selectOne获取记录,如果返回值类型是集合对象,生成的代理对象内部通过selectList获取记录

public interface UserMapper {
    // 调用selectOne获取记录
	public User findUserById(int id) throws Exception;
    // 调用selectList获取记录
	public List<User> findUserByName(String username) throws Exception;

    <select id="findUserById" resultType="user" parameterType="int">
		select * from user where id = #{id}
	</select>
	<!-- 根据姓名查询用户信息 -->
	<select id="findUserByName" resultType="user" parameterType="String">
		select * from user where username = '%${username}%'
	</select>

6、返回值问题

调用statement,返回多条记录,mapper.java中方法的返回类型不能是pojo类型,因为是pojo类型,底层会调用selectOne函数查询结果,会报错!!

7、输入参数问题(解决Mapper.java中方法只能输入一个参数的问题)

可以通过扩展pojo(自己定义pojo包装类型,将要传入的参数传入进去。)

public List<User> findUserList(UserQuery userQuery) throws Exception;

public class UserQueryVo {
	private User user;
	private UserCustom userCustom;
	private List<Integer> ids;


public class UserCustom extends User {
	
}


    //测试findUserList
	@Test
	public void testFindUserList() throws Exception{
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	
		UserQueryVo userQueryVo = new UserQueryVo();
		
		UserCustom userCustom = new UserCustom();
		userCustom.setUsername("小明");
		userQuery.setUserCustom(userCustom);
		
		List<User> list = userMapper.findUserList(userQueryVo);
		sqlSession.close();
		System.out.println(list);
	}

mybatis开发小结:

1、编写SqlMapConfig.xml :引入数据库文件,引入mapper.xml文件

2、编写mapper.xml文件:定义statement,封装成MappedStatement对象返回。

3、编程通过配置文件创建SqlSessionFactory

4、通过sqlSessionFactory获取SqlSession

5、通过SqlSession操作数据库

6、关闭SqlSession!!

因为插入记录需要一个主键返回:有两种方式

mapper.xml文件中

通过LAST_INSERT_ID()获取刚插入记录的自增主键值,在insert语句执行后,执行select LAST_INSERT_ID()就可以获取自增主键

通过使用mysql的uuid机制生成主键:使用uuid生成主键的好处是不考虑数据库移植后主键冲突问题。

    <!-- 添加用户 -->
	<insert id="insertUser" parameterType="cn.itcast.mybatis.pojo.User">
		<!-- 通过LAST_INSERT_ID()获取刚插入记录的自增主键值 -->
        <!-- selectKey:用于生成主键返回,定义了获取主键的sql-->
        <!-- order:设置selectKey中sql执行的顺序,相对于insert来说-->
        <!-- 将主键设置到哪个属性-->
        <!-- select结果类型-->
		<!-- <selectKey keyProperty="id" order="AFTER" resultType="int">
			select LAST_INSERT_ID();
		</selectKey> -->
		<!-- 通过uuid生成主键 -->
		<selectKey keyProperty="id" resultType="string" order="BEFORE">
			select uuid();
		</selectKey>
		Insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
	</insert>

 

SqlSessionFactory(会话工厂)

作用:创建SqlSessionFactory

SqlSession:(面向用户的接口,数据库操作方法)

作用:操作数据库

Executor(数据库操作的执行器)

executor是一个接口,有两个实现(默认执行器和缓存执行器)

MappedStatement(mybatis的封装对象)

作用:封装sql语句

在mapper.xml中封装的sql语句返回的就是mappedStatement对象

每一个select就是mappedStatement对象

    <!-- 根据id查询用户信息 -->
	<!-- id:唯一表示statement -->
	<select id="findUserById" resultType="user" parameterType="int">
        <!--通过占位符传递参数 -->
		select * from user where id = #{id}
	</select>
	<!-- 根据姓名查询用户信息 -->
	<select id="findUserByName" resultType="user" parameterType="String">
        <!--通过拼接传递参数-->
		select * from user where username = '%${username}%'
	</select>

mybatis解决jdbc编程问题

1、数据库创建连接、释放频繁造成系统资源浪费从而影响性能,所以使用数据库连接池

解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库连接。

2、Sql语句写在代码中造成代码不易维护,实际应用啥情况sql变化的可能较大,sql变动需要改变java代码

解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

3、向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。

4、对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

resultMap(返回类型)

resultType与resultMap对比:

resultType:指定输出结果的类型,将sql查询结果映射为java对象。

注意:使用resultType时,sql查询的列名要和resultType指定的pojo的属性名相同,指定相同的属性才可以映射成功,如果sql查询的列名要和resultType指定的pojo的属性名全部不相同,list中无法创建pojo对象。

resultMap:将查询结果映射为java对象

如果查询列名和最终要映射的pojo的属性名不一致,使用resultMap将列名和pojo的属性名做一个对应关系(列名和属性名映射设置)

resultMap

<!-- 列名和属性名映射设置  column:别名  property:user中的属性名称-->
	<resultMap id="userListResultMap" type="user">
    <!-- 列名
    id_,username_,birthday
    column:运行sql语句后的列名
    property:type指定的哪个属性-->
		<id column="id_" property="id"/>
		<result column="username_" property="username"/>
		<result column="birthday_" property="birthday"/>
	</resultMap>

使用resultMap

<!-- 使用resultMap进行映射后的查询 -->
	<select id="findUserListResultMap" parameterType="userQueryVo" resultMap="userListResultMap">
		select id id_,username username_,birthday birthday_ from user where username like '%${userCustom.username}%'
	</select>

动态sql

#{}和${}完成输入参数的属性值获取,通过OGNL获取parameterType指定pojo的属性名。

#{}:占位符号,好处防止sql注入

${}:sql拼接符号

if和where

通过sql片段可以将sql语句抽取出来,单独定义,在其他statement中可以引用sql片段。

    <!-- 动态sql -->
	<select id="sql" parameterType="userQueryVo" resultType="user">
		select * from user where
		<where>
			<!-- 引用sql片段 -->
			<include refid="query_user_where"></include>
		</where>
	</select>
	
	<!-- 将用户查询条件定义为sql片段,提取出来方便引用 -->
	<sql id="query_user_where">
		<if test="userCustom!=null">
				<if test="userCustom.username != null and userCustom,username != ''">
					and username like '%${userCustom.username}%'
				</if>
				<if test="userCustom.sex != null and userCustom.sex != ''">
					and sex = #{userCustom.sex}
				</if>
				<!-- 还有很多查询条件 -->
			</if>
	</sql>


 <!-- 动态sql -->
<select id="sql" parameterType="userQueryVo" resultType="user">
	select * from user where
	<where>
	    <sql id="query_user_where">
		    <if test="userCustom!=null">
				<if test="userCustom.username != null and userCustom,username != ''">
					and username like '%${userCustom.username}%'
				</if>
				<if test="userCustom.sex != null and userCustom.sex != ''">
					and sex = #{userCustom.sex}
				</if>
				<!-- 还有很多查询条件 -->
		    </if>
	    </sql>
	</where>
</select>
	
// 这个查询相当于
select * from user where username like '%${userCustom.username}%' and sex = #{userCustom.sex};

foreach

在statement通过foreach遍历parameterType中的集合类型

<!-- 
collection : 集合的属性 就是pojo类型中的哪个集合属性
open:开始循环的拼接字符串本例是AND id IN(
close:结束循环拼接的串
item:每次循环获取的对象
separator:每两次循环中间拼接的串
-->
<select id="findUserByIdGroup" parameterType="userQueryVo" resultType="User">
		select id,username,birthday from user where username like '%小明%'
		<foreach collection="ids" open="AND id IN(" close=")" item="id" separator=",">
			#{id}
		</foreach>
</select>

//相当于
select id,username,birthday from user where username like '%小明%' and id in(id1,id2,id3);

<select id="findUserByIdGroup" parameterType="userQueryVo" resultType="User">
		select id,username,birthday from user where username like '%小明%'
        <foreach collection="ids" open="and (" colse=")" item="id" separator="or">
            id=#{id}
</select>

//相当于
select id,username,birthday from user where username like '%小明%' and (id = 16 or id = 24 or id = 36);

resultMap完成一对一、一对多、多对多查询

当关联多个表进行查询的时候,一般resultType无法完成,多使用resultMap完成

resultType实现一对一查询:先确定主查询表 orders,然后确定关联信息表user,通过外键确定连接  

// 查询语句:select orders.*,user.username,user.sex from orders,user where orders.user_id = user.id;

根据查询语句的返回类型确定扩展的pojo类型

public class OrderCustom extends Orders{
    //补充用户信息
    private String username;
    private String sex;
}

mapper.xml中编写:

<select id="findOrderUserList" resultType="OrderCustom">
    //查询语句
</select>

mapper.java

public interface OrdersMapperCustom{
    public List<OrderCustom> findOrderUserList() throws Exception;
}

使用resultMap实现一对一

resultMap提供一对一关联查询的映射和一对多关联查询映射,一对一映射思路:将关联查询的信息映射到pojo中。

下列查询语句就是讲关联信息的pojo映射到主查询信息pojo中

// 查询语句:select orders.*,user.username,user.sex from orders,user where orders.user_id = user.id;
public class Orders{
    private Integer id;
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;

    //关联用户信息
    private User user;
}

mapper.xml

    <!-- 一对一查询使用resultMap完成
	查询订单关联查询用户信息 -->
	<select id="findOrderUserListResultMap" resultMap="ordersUserResultMap">
		SELECT orders.*,user.username,user.sex FROM orders,USER 
		WHERE orders.user_id = user.id;
	</select>

resultMap定义:

    <!-- 一对一查询resultMap -->
	<resultMap type="orders" id="ordersUserResultMap">
		<!-- orders类中的属性进行映射 -->
		<id column="id" property="id"/>
		<!-- orders类中的userId对应table中的user_id column:别名 -->
		<result column="user_id" property="userId"/>
		<result column="number" property="number"/>
		<result column="createtime" property="createtime"/>
		<result column="note" property="note"/>
		
		<!-- 关联信息的映射 
        association:用于关联到单个的pojo
        property:要将关联信息映射到orders的哪个属性中
        javaType:关联信息映射到orders的属性的类型,是user类型
        -->
		<association property="user" javaType="user">
			<id column="user_id" property="id"/>
			<result column="username" property="username"/>
			<result column="sex" property="sex"/>
		</association>
	</resultMap>

//SqlMapConfig.xml中定义别名  没有定义别名记得写全限定名
<typeAlias type="cn.itcast.mybatis.pojo.Orders" alias="orders"/>
<typeAlias type="cn.itcast.mybatis.pojo.User" alias="user"/>

resultType:要自定义pojo保证sql查询列和pojo的属性对应,这种方法简单

resultMap:使用association完成一对一映射需要设置一个resultMap,如果要实现延迟加载就只能用resultMap实现。

resultMap实现一对多查询:

// 查询语句:select orders.*,user.username,user.sex from orders,user where orders.user_id = user.id;

//在这个语句的基础上加一个表关联
select orders.*,user.username,user.sex,orderdetail.id ordertail_id,orderdetail.items_num,orderdetail.items_id from orders,user,orderdetail where orders.user_id = user.id and orders_id = orderdetail.orders_id;
public class Orders{
    private Integer id;
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;

    //关联用户信息
    private User user;
    private List<Orderdetail> orderdetails;
}

 

resultMap定义:

    <!-- 一对多查询resultMap定义
       extends:我们上面定义的一对一resultMap
        ofType:集合中的pojo类型
     -->
	<resultMap type="orders" id="orderAndOrderDetails" extends="ordersUserResultMap">
		<collection property="orderdetails" ofType="cn.itcast.mybatis.pojo.Orderdetail">
			<id column="orderdetail_id" property="id"/>
			<result column="items_num" property="itemsNum"/>
			<result column="items_id" property="itemsId"/>
		</collection>
	</resultMap>

//注意这里用的是collection !! 不是association

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值