Mybatis入门

Mybati入门

mybatis概述

  • 学习过的持久层框架:DBUtils , Hibernate

  • Mybatis就是类似于hibernate的orm持久层框架。

  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

  • Mybatis是面向sql的持久层框架,他封装了jdbc访问数据库的过程,我们开发,只需专注于sql语句本身的拼装,其它复杂的过程全部可以交给mybatis去完成。

  • 为什么学Mybatis?

  1. Hibernate学习门槛不低,要精通门槛更高。门槛高在怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate缓存与数据加载策略方面需要你的经验和能力都很强才行
  2. sql优化方面,Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。当然了,Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。说得更深入一些,如果有个查询要关联多张表,比如5张表,10张表时,而且,我们要取的字段只是其中几张表的部分字段。这时用hibernate时就会显得非常力不从心。

jdbc在编程中的问题

  • 创建数据库

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(32) NOT NULL COMMENT '用户名称',
      `birthday` date DEFAULT NULL COMMENT '生日',
      `sex` char(1) DEFAULT NULL COMMENT '性别',
      `address` varchar(256) DEFAULT NULL COMMENT '地址',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
    
  • 编写测试代码

    //这一段也是要求掌握的(比如手写)
    public class JDBCTest {
    	public static void main(String[] args) {
    		Connection conn = null;
    		// PreparedStatement是预编译的Statement,通过Statement发起数据库的操作
    		// PreparedStatement防止sql注入,执行数据库效率高(数据库的)
    		PreparedStatement pst = null;
    		ResultSet rs = null;
    		try {
    			// 1、加载数据库驱动
    			Class.forName("com.mysql.jdbc.Driver");
    			// 2、通过数据库驱动管理类获取数据库连接
    			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root",
    					"root");
    			// 定义sql语句 ?表示占位符
    			String sql = "select * from user where username = ?";
    			// 3、根据数据库连接对象获取
    			pst = conn.prepareStatement(sql);
    
    			// 4、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
    			pst.setString(1, "张三丰");
    			// 5、向数据库发出sql执行查询,查询出结果集
    			rs = pst.executeQuery();
    			// 6、遍历数据集
    			while (rs.next()) {
    				System.out.println("姓名:" + rs.getString(1) + ",地址:" + rs.getString("address"));
    			}
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			// 释放资源
    			if (conn != null) {
    				try {
    					conn.close();
    				} catch (SQLException e) {
    					e.printStackTrace();
    				}
    			}
    			if (pst != null) {
    				try {
    					pst.close();
    				} catch (SQLException e) {
    					e.printStackTrace();
    				}
    			}
    			if (rs != null) {
    				try {
    					rs.close();
    				} catch (SQLException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    
  • JDBC在编程中的问题和解决办法

    1、数据库连接频繁的创建和关闭,缺点浪费数据库的资源,影响操作效率

    解决:使用数据库连接池

    2、sql语句是硬编码,如果需求变更需要修改sql,就需要修改java代码,需要重新编译,系统不易维护。

    解决:将sql语句 统一配置在文件中,修改sql不需要修改java代码。

    3、通过preparedStatement向占位符设置参数,存在硬编码( 参数位置,参数)问题。系统不易维护。

    解决:将sql中的占位符及对应的参数类型配置在配置文件中,能够自动输入 映射。

    4、遍历查询结果集存在硬编码(列名)。

    解决:自动进行sql查询结果向java对象的映射(输出映射)。

mybatis架构

  1. Mybatis架构图
    在这里插入图片描述
  • 结构图解释:
  1. mybatis配置

    SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。

    mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。

    SqlMapConfig.xml是mybatis的核心文件。mybatis将dao层与sql语句分离开来,虽然写的时候分离开来了,但是执行的时候还是要依靠sql语句,所以我们的sql语句写在Mapper.xml中。我们在加载核心的时候,会加载他下面的Mapper.xml,所以sql语句便会加载进去了。我们只需要在SqlMapConfig.xml中引入Mapper.xml就可以了,所以最后只需要加载SqlMapConfig.xml这一个核心配置文件。

  2. 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂。工厂能帮我们去加载核心配置文件。加 载了核心配置文件后就创建session,通过session可以对数据库进行操作。

  3. 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。

  4. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。Executor是执行者,我们不需要管,因为mybatis已经为我们封装好了。mybatis直接执行sql语句。

  5. Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。 mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。

  6. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。

  7. Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

  8. Mapped Statement是输入与输出中间过程中产生的一些对象,通过这些对象去访问数据库。

总结:工厂能帮我们去加载核心配置文件,同时创建会话,会话里面有执行者,执行sql语句,在执行sql语句的过程中产生对象,通过Mapped Statement封装成对象。执行sql语句就要需要输入参数和输出参数。

mybatis入门程序

  • 需求:

    实现用户查询:

    1. 根据用户id(主键)查询用户信息(单条记录)
    2. 根据用户名称模糊查询用户信息(多条记录)
    3. 用户添加
    4. 用户删除
    5. 用户修改

根据用户id(主键)查询用户信息(单条记录)

  • 导入jar包
    在这里插入图片描述

  • 包结构
    在这里插入图片描述

  • 配置log4j.properties文件

    # Global logging configuration,建议开发环境中要用debug
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    
    
  • 配置Mybatis的核心配置文件SqlMapConfig.xml

    通过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>
        
    	<!-- 和spring整合后 environments配置将废除 -->
    	<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/mybatis?characterEncoding=utf-8"/>
    				<property name="username" value="root" />
    				<property name="password" value="root" />
    			</dataSource>
    		</environment>
    	</environments>
        
        <!--加载mapper映射
    		如果将和spring整合后,可以使用整合包中提供的mapper扫描器,此处的mappers不用配置了。
    	 -->
    	<mappers>
    		<!-- 第一种:通过resource引用mapper的映射文件 -->
    		 <mapper resource="sqlmap/User.xml" />
    	</mappers>
        
    </configuration>
    
  • pojo(User.java)

    Public class User {
    	private int id;
    	private String username;// 用户姓名
    	private String sex;// 性别
    	private Date birthday;// 生日
    	private String address;// 地址
    	get/set方法	
    }
    
    
  • User.xml(重点)

    建议命名规则:表名+mapper.xml

    早期ibatis命名规则:表名.xml

    我们逐步进行转化

    <?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命名空间,为了对sql语句进行隔离,方便管理 ,mapper开发dao方式,使用namespace有特殊作用 -->
        
    <mapper namespace="user">
    
    <!-- 在mapper.xml文件中配置很多的sql语句,执行每个sql语句时,封装为MappedStatement对象 mapper.xml以statement为单位管理sql语句 -->
    
    	<!-- 根据id查询用户信息 -->
        
    	<!-- id:唯一标识 一个statement #{}:表示 一个占位符,如果#{}中传入简单类型的参数,#{}中的名称随意 parameterType:输入 
    		参数的类型,通过#{}接收parameterType输入 的参数 resultType:输出结果 类型,不管返回是多条还是单条,指定单条记录映射的pojo类型 -->
        
    	<select id="findUserById" parameterType="int"
    		resultType="com.syj.mybatis.pojo.User">
    		SELECT * FROM USER WHERE id = #{id};
    	</select>
    </mapper>
    
  • 编写测试代码

    根据流程图我们得到

    SqlSessionFactory一旦被创建,应该在应用程序执行期间都存在,不要重复创建多次,建议使用单例模式。SqlSession的实例不能被共享,也是线程不安全的。(最好在方法体内使用)

    所有我们测试的时候可以将SqlSessionFactory进行抽取

    @Before在方法执行之前都会先进行加载

    public class MybatiBase {
    	private SqlSessionFactory sqlSessionFactory;
    
    	// 创建回话工厂
    	@Before
    	public void init() throws IOException {
    		// 1、Mybatis的核心配置文件
    		String resource = "SqlMapConfig.xml";
    		// 2、加载Mybatis的配置文件
    		InputStream is = Resources.getResourceAsStream(resource);
    		// 3、获取SqlSessionFactory对象,创建会话工厂,加载配置文件到输入 流
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    	}
    
    	// 测试根据Id查询用户信息(得到单个数据)
    	@Test
    	public void findUserById() {
    		// 1、通过sqlSessionFactory创建sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 通过sqlSession操作数据库
    		// 第一个参数:statement的位置,等于namespace+statement的id
    		// 第二个参数:传入的参数
    		User user = null;
    		try {
    			user = sqlSession.selectOne("user.findUserById", 16);
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    		System.out.println(user);
    	}
    }
    

根据用户名查找用户列表

  • 映射文件与sql

    <!-- 根据用户名称查询用户信息,可能返回多条
    		${}:表示sql的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在sql中。
    -->
    	<select id="findUserByName" parameterType="String" 	resultType="com.syj.mybatis.pojo.User">
    		SELECT * FROM USER WHERE username like '%${value}%';
    	</select>
    
  • 编码进行代码测试

    	// 测试模糊查询用户信息
    	@Test
    	public void findUserByName() {
    		// 1、通过sqlSessionFactory创建sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 通过sqlSession操作数据库
    		// 第一个参数:statement的位置,等于namespace+statement的id
    		// 第二个参数:传入的参数
    		List<User> list = null;
    		try {
    			list = sqlSession.selectList("user.findUserByName", "小明");
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    		for (User user : list) {
    			System.out.println(user);
    		}
    	}
    

添加用户

  • 映射文件与sql

    <!-- 保存用户 -->
    <!--
        parameterType:输入的参数的数据类型,需要包含username,birthday,sex,address四个属性,使用User
        #{}:接收pojo数据,可以使用OGNL表达式解析出对象的属性值
        #{username}:表示从parameterType中获取属性值
    -->
    	<insert id="insertUser" parameterType="com.syj.mybatis.pojo.User" >
    	INSERT INTO USER (username,birthday,sex,address) VALUES	(#{username},#{birthday},#{sex},#{address})
    	</insert>
    
  • 编码进行代码测试

    // 测试保存用户信息
    	@Test
    	public void insertUserByName() {
    		// 1、通过sqlSessionFactory创建sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 通过sqlSession操作数据库
    		// 第一个参数:statement的位置,等于namespace+statement的id
    		// 第二个参数:传入的参数
    		User user = new User();
    		user.setUsername("芙蓉");
    		user.setSex("2");
    		user.setBirthday(new Date());
    		user.setAddress("北京三里屯");
    
    		try {
    			sqlSession.insert("user.insertUser", user);
    			// 提交事务
    			sqlSession.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    		System.out.println("用户的id:" + user.getId());
    	}
    
  • mysql自增主键返回

    需求:user对象插入到数据库后,新记录的主键要通过user对象返回,通过user获取主键值。

    解决思路:

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

    user.xml的修改

    	<!-- 保存用户 -->
    	<!--
    		parameterType:输入的参数的数据类型,需要包含username,birthday,sex,address四个属性,使用User
    		#{}:接收pojo数据	,可以使用OGNL表达式解析出对象的属性值
    		#{username}:表示从parameterType中获取属性值	
    	-->
    	<insert id="insertUser" parameterType="com.syj.mybatis.pojo.User" >
    		<!--
                selectKey:用于进行主键返回,定义了获取主键值的sql
                order:设置selectKey中sql执行的顺序,相对于insert语句来说
                keyProperty	: 将主键值设置到哪个属性
                resultType:select LAST_INSERT_ID()的结果 类型
    		  -->
    		<selectKey keyProperty="id" order="AFTER"  resultType="int">
    				SELECT  LAST_INSERT_ID()
    		</selectKey>
    		
    		INSERT INTO USER (username,birthday,sex,address) VALUES	(#{username},#{birthday},#{sex},#{address})
    	</insert>
    
  • Mysql的uuid返回主键

    在使用uuid之前数据库user表要先加上uuid2字段、user的pojo也要加上相应属性

    	<!-- mysql的uuid生成主键 -->
    	<insert id="insertMysqlUUID">
    		<selectKey keyProperty="uuid2" order="BEFORE" resultType="String" >
    			SELECT  UUID()
    		</selectKey>
    			INSERT INTO USER (username,birthday,sex,address,uuid2) VALUES	(#{username},#{birthday},#{sex},#{address},#{uuid2})
    	</insert>
    

    编码进行代码测试

    	// 测试保存用户信息(测试myslq的UUID)
    	@Test
    	public void insertUserByUUID() {
    		// 1、通过sqlSessionFactory创建sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 通过sqlSession操作数据库
    		// 第一个参数:statement的位置,等于namespace+statement的id
    		// 第二个参数:传入的参数
    		User user = new User();
    		user.setUsername("王昭君");
    		user.setSex("2");
    		user.setBirthday(new Date());
    		user.setAddress("北京西二旗");
    
    		try {
    			sqlSession.insert("user.insertMysqlUUID", user);
    			// 提交事务
    			sqlSession.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    		System.out.println("用户的uuid2:" + user.getUuid2());
    	}
    
  • 实现 oracle数据库主键返回

    oracle没有自增主键机制,使用序列完成主键生成。

    实现思路:

    先查询序列得到主键,将主键设置到user对象中,将user对象插入数据库。

    <!-- oracle
    		在执行insert之前执行select 序列.nextval() from dual取出序列最大值,将值设置到user对象 的id属性
    	 -->
    <insert id="insertOracleUser">
    	<selectKey keyProperty="" order="BEFORE" resultType="" >
    		select  序列.nextval from dual
    	</selectKey>
    	INSERT INTO USER (username,birthday,sex,address) VALUES	(#{username},#{birthday},#{sex},#{address})
    </insert> 
    

用户删除

  • 映射文件和sql

    	<!-- 删除用户 -->
    	<delete id="deleteUser" parameterType="com.syj.mybatis.pojo.User" >
    		DELETE FROM USER WHERE id = #{id};
    	</delete>
    
  • 编码进行代码测试

    // 测试根据id删除用户
    	@Test
    	public void deleteUser() {
    		// 1、通过sqlSessionFactory创建sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 通过sqlSession操作数据库
    		// 第一个参数:statement的位置,等于namespace+statement的id
    		// 第二个参数:传入的参数
    		try {
    			sqlSession.delete("user.deleteUser", 29);
    			// 提交事务
    			sqlSession.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    	}
    

用户更新

  • 映射文件和sql

    <!-- 更新用户 -->
    	<update id="updateUser" parameterType="com.syj.mybatis.pojo.User">
    		UPDATE USER SET username = #{username},birthday=#{birthday},address=#{address}  WHERE id= #{id}	
    	</update>
    
  • 编码进行代码测试

    // 测试根据id更新用户姓名
    	@Test
    	public void updateUser() {
    		// 1、通过sqlSessionFactory创建sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 通过sqlSession操作数据库
    		// 第一个参数:statement的位置,等于namespace+statement的id
    		// 第二个参数:传入的参数
    
    		User user = new User();
    		user.setId(27);
    		user.setUsername("金毛狮王");
    		user.setBirthday(new Date());
    		user.setAddress("河南洛阳");
    		try {
    			sqlSession.update("user.updateUser", user);
    			// 提交事务
    			sqlSession.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    		System.out.println("用户ID:" + user.getId());
    	}
    

Mybatis开发过程小节

  • 步骤:

​ 1、编写SqlMapConfig.xml

​ 2、编写mapper.xml,定义了statement

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

​ 4、通过SqlSessionFactory获取SqlSession

​ 5、通过SqlSession操作数据库,如果执行添加、更新、删除需要调用SqlSession.commit()

​ 6、SqlSesion使用完成要关闭

  • 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定义输出结果的类型。

  • mybatis与hibernate重要区别

    企业开发进行技术选型 ,考虑mybatis与hibernate适用场景。

    1、mybatis:入门简单,程序容易上手开发,节省开发成本 。mybatis需要程序员自己编写sql语句,是一个不完全 的ORM框架,对sql修改和优化非常容易实现 。

    2、mybatis适合开发需求变更频繁的系统,比如:互联网项目。

    3、hibernate:入门门槛高,如果用hibernate写出高性能的程序不容易实现。hibernate不用写sql语句,是一个 ORM框架。

    4、hibernate适合需求固定,对象数据模型稳定,中小型项目,比如:企业OA系统。

    总之,企业在技术选型时根据项目实际情况,以降低成本和提高系统 可维护性为出发点进行技术选型。

  • SqlMapConfig.xml

    是mybatis全局配置文件,只有一个,名称不固定的,主要mapper.xml,mapper.xml中配置 sql语句

  • mapper.xml

    mapper.xml是以statement为单位进行配置。(把一个sql称为一个statement),satatement中配置 sql语句、parameterType输入参数类型(完成输入映射)、resultType输出结果类型(完成输出映射)。

    还提供了parameterMap配置输入参数类型(过期了,不推荐使用了)

    还提供resultMap配置输出结果类型(完成输出映射),明天重点讲通过resultMap完成复杂数据类型的映射(一对多,多对多映射)

  • #{}

    表示一个占位符,向占位符输入参数,mybatis自动进行java类型和jdbc类型的转换。

    程序员不需要考虑参数的类型,比如:传入字符串,mybatis最终拼接好的sql就是参数两边加单引号。

    #{}接收pojo数据,可以使用OGNL解析出pojo的属性值

  • ${}

    表示sql的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在sql中。

    ${}也可以接收pojo数据,可以使用OGNL解析出pojo的属性值

    缺点:不能防止sql注入。

mybatis开发dao的方法

SqlSession作用范围

  • SqlSessionFactoryBuilder

    SqlSessionFactoryBuilder是以工具类方式来使用,需要创建sqlSessionFactory就new一个SqlSessionFactoryBuilder。

  • sqlSessionFactory

    正常开发时,以单例方式管理sqlSessionFactory,整个系统运行过程中sqlSessionFactory只有一个实例,将来和spring整合后由spring以单例方式管理sqlSessionFactory。

  • sqlSession

    sqlSession是一个面向用户(程序员)的接口,程序员调用sqlSession的接口方法进行操作数据库。

    sqlSession能否以单例 方式使用??

    由于sqlSession是线程不安全,所以sqlSession最佳应用范围在方法体内,在方法体内定义局部变量使用sqlSession。

原始dao开发方式

  • 需求:

    实现用户查询:

    1. 根据用户id(主键)查询用户信息(单条记录)
    2. 根据用户名称模糊查询用户信息(多条记录)
    3. 用户添加
    4. 用户删除
    5. 用户修改
  • 编写用户的接口

    /**
     * 用户的接口Dao
     * 
     * @author SYJ
     *
     */
    public interface UserDao {
    	// 根据id查询用户的信息
    	public User findUserById(int id) throws Exception;
    
    	// 根据姓名进行模糊查询
    	public List<User> findUserByName(String username) throws Exception;
    
    	// 更新用户
    	public void updateUser(User user) throws Exception;
    
    	// 删除用户
    	public void deleteUser(int id) throws Exception;
    }
    
  • UserDao接口的实现类

    public class UserDaoImpl implements UserDao {
    	private SqlSessionFactory sqlSessionFactory;
    
    	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
    		this.sqlSessionFactory = sqlSessionFactory;
    	}
    
    	/**
    	 * 根据id查询用户信息
    	 */
    	@Override
    	public User findUserById(int id) throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		User user = sqlSession.selectOne("user.findUserById", id);
    		sqlSession.close();
    		return user;
    	}
    
    	/**
    	 * 根据姓名对用户进行模糊查询
    	 */
    	@Override
    	public List<User> findUserByName(String username) throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		List<User> list = sqlSession.selectList("user.findUserByName", username);
    		sqlSession.close();
    		return list;
    	}
    	/**
    	 * 更新用户信息
    	 */
    	@Override
    	public void updateUser(User user) throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		sqlSession.update("user.updateUser", user);
    		sqlSession.commit();
    		sqlSession.close();
    	}
    	/**
    	 * 删除用户信息
    	 */
    	@Override
    	public void deleteUser(int id) throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		sqlSession.delete("user.deleteUser", id);
    		sqlSession.commit();
    		sqlSession.close();
    	}
    }
    
  • 编码进行代码的测试

    public class UserDaoTest {
    	private SqlSessionFactory sqlSessionFactory;
    
    	// 创建回话工厂
    	@Before
    	public void init() throws IOException {
    		// 1、Mybatis的核心配置文件
    		String resource = "SqlMapConfig.xml";
    		// 2、加载Mybatis的配置文件
    		InputStream is = Resources.getResourceAsStream(resource);
    		// 3、获取SqlSessionFactory对象,创建会话工厂,加载配置文件到输入 流
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    	}
    
    	@Test
    	public void testFindUserById() throws Exception {
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    		User user = userDao.findUserById(25);
    		System.out.println(user);
    	}
    
    	@Test
    	public void testFindUserByName() throws Exception {
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    		List<User> list = userDao.findUserByName("小明");
    		for (User user : list) {
    			System.out.println(user);
    		}
    	}
    
    	@Test
    	public void testUpdateUser() throws Exception {
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    
    		User user = new User();
    		user.setId(27);
    		user.setUsername("谢逊");
    		user.setBirthday(new Date());
    		user.setAddress("上海浦东");
    
    		userDao.updateUser(user);
    	}
    
    	@Test
    	public void testDeleteUser() throws Exception {
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    		userDao.deleteUser(34);
    	}
    }
    

mapper代理的方式

  • 动态代理dao开发规则

    1. mapper.xml中namespace指定为mapper接口的全限定名
      在这里插入图片描述
      此步骤目的:通过mapper.xml和mapper.java进行关联。

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

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

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

  • mapper.xml(映射文件)

    mapper映射文件的命名方式建议:表名Mapper.xml------>UserMapper.xml

    namespace指定为mapper接口的全限定名

    <?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命名空间,为了对sql语句进行隔离,方便管理 ,mapper开发dao方式,使用namespace有特殊作用 -->
    <mapper namespace="com.syj.mybatis.mapper.UserMapper">
    
    	<!-- 在mapper.xml文件中配置很多的sql语句,执行每个sql语句时,封装为MappedStatement对象 mapper.xml以statement为单位管理sql语句 -->
    
    	<!-- 根据id查询用户信息====================== -->
    
    	<!-- id:唯一标识 一个statement #{}:表示 一个占位符,如果#{}中传入简单类型的参数,#{}中的名称随意 parameterType:输入 
    		参数的类型,通过#{}接收parameterType输入 的参数 resultType:输出结果 类型,不管返回是多条还是单条,指定单条记录映射的pojo类型 -->
    	<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>
    
    
    	<!-- 保存用户======================  -->
    	<!--
    		parameterType:输入的参数的数据类型,需要包含username,birthday,sex,address四个属性,使用User
    		#{}:接收pojo数据	,可以使用OGNL表达式解析出对象的属性值
    		#{username}:表示从parameterType中获取属性值
    		
    	-->
    	<insert id="insertUser" parameterType="user" >
    		<!--
    				selectKey:用于进行主键返回,定义了获取主键值的sql
    				order:设置selectKey中sql执行的顺序,相对于insert语句来说
    				keyProperty	: 将主键值设置到哪个属性
    				resultType:select LAST_INSERT_ID()的结果 类型
    		  -->
    		<selectKey keyProperty="id" order="AFTER"  resultType="int">
    				SELECT  LAST_INSERT_ID()
    		</selectKey>
    		
    		INSERT INTO USER (username,birthday,sex,address) VALUES	(#{username},#{birthday},#{sex},#{address})
    	</insert>
    	<!-- mysql的uuid生成主键 -->
    	<insert id="insertMysqlUUID">
    		<selectKey keyProperty="uuid2" order="BEFORE" resultType="String" >
    			SELECT  UUID()
    		</selectKey>
    			INSERT INTO USER (username,birthday,sex,address,uuid2) VALUES	(#{username},#{birthday},#{sex},#{address},#{uuid2})
    	</insert>
    	
    	
    	<!-- oracle
    		在执行insert之前执行select 序列.nextval() from dual取出序列最大值,将值设置到user对象 的id属性
    	 -->
    	<!-- <insert id="insertOracleUser">
    		<selectKey keyProperty="" order="BEFORE" resultType="" >
    			select  序列.nextval from dual
    		</selectKey>
    		INSERT INTO USER (username,birthday,sex,address) VALUES	(#{username},#{birthday},#{sex},#{address})
    	</insert> -->
    	
    	
    	<!-- 删除用户======================  -->
    	<delete id="deleteUser" parameterType="user" >
    		DELETE FROM USER WHERE id = #{id};
    	</delete>
    	
    	<!-- 更新用户======================  -->
    	<update id="updateUser" parameterType="user">
    		UPDATE USER SET username = #{username},birthday=#{birthday},address=#{address}  WHERE id= #{id}	
    	</update>
    	
    </mapper>
    
  • mapper接口

    mybatis提出了mapper接口,相当 于dao 接口。

    mapper接口的命名方式建议:表名Mapper------>UserMapper.java

    /**
     * 用户的接口Dao
     * 
     * @author SYJ
     *
     */
    public interface UserMapper {
    	// 根据id查询用户的信息
    	public User findUserById(int id) throws Exception;
    
    	// 根据姓名进行模糊查询
    	public List<User> findUserByName(String username) throws Exception;
    
    	// 更新用户
    	public void updateUser(User user) throws Exception;
    
    	// 删除用户
    	public void deleteUser(int id) throws Exception;
    }
    
  • 将mapper.xml在SqlMapConfig.xml中加载

    <mappers>
    		<!-- 第一种:通过resource引用mapper的映射文件 -->
    		 <mapper resource="sqlmap/User.xml" />
    		<mapper resource="mapper/UserMapper.xml" /> 
    </mappers>
    
  • mapper接口返回单个对象和集合对象

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

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

  • 常见输入参数和返回值问题

    返回值问题:

    如果方法调用的statement,返回是多条记录,而mapper.java方法的返回值为pojo,此时代理对象通过selectOne调用,由于返回多条记录,所以报错:

    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
    

    输入参数问题

    使用mapper代理的方式开发,mapper接口方法输入 参数只有一个,可扩展性是否会差

    可扩展性没有问题,因为dao层就是通用的,可以通过扩展pojo(定义pojo包装类型)将不同的参数(可以是pojo也可以简单类型)传入进去。

sqlMapConfig.xml配置

  • 任何框架的初始化,无非是加载自己运行时所需要的配置信息。MyBatis的配置信息,大概包含以下信息,其高层级结构如下:

    properties(属性)
    settings(全局配置参数)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境集合属性对象)
    	environment(环境子属性对象)
    		transactionManager(事务管理)
    		dataSource(数据源)
    mappers(映射器)
    
  • properties属性的定义

    可以把一些通用的属性值配置在属性文件中,加载到mybatis运行环境内。

    比如:创建db.properties配置数据库连接参数。
    在这里插入图片描述

    注意: MyBatis 将按照下面的顺序来加载属性:

    • 在 properties元素体内定义的属性首先被读取。
    • 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性
    • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

    建议使用properties,不要在properties中定义属性,只引用定义的properties文件中属性,并且properties文件中定义的key要有一些特殊的规则。

  • setting全局参数配置

    mybatis运行时可以调整一些全局参数(相当于软件的运行参数)

    根据使用需求进行参数配置。

    注意:小心配置,配置参数会影响mybatis的执行。

    ibatis的全局配置参数中包括很多的性能参数(最大线程数,最大待时间。。。),通过调整这些性能参数使ibatis达到高性能的运行,mybatis没有这些性能参数,由mybatis自动调节。

  • 别名typeAliases

    可以将parameterType、resultType中指定的类型通过别名引用。

    • mybaits提供了很多别名

      别名映射的类型
      _bytebyte
      _longlong
      _shortshort
      _intint
      _integerint
      _doubledouble
      _floatfloat
      _booleanboolean
      stringString
      byteByte
      longLong
      shortShort
      intInteger
      integerInteger
      doubleDouble
      floatFloat
      booleanBoolean
      dateDate
      decimalBigDecimal
      bigdecimalBigDecimal
    • 自定义别名
      在这里插入图片描述

    • 使用别名
      在这里插入图片描述

    • typeHandlers

      类型处理器将java类型和jdbc类型进行映射。

      Type HandlerJava TypesJDBC Types
      BooleanTypeHandlerjava.lang.Boolean, booleanAny compatible BOOLEAN
      ByteTypeHandlerjava.lang.Byte, byteAny compatible NUMERIC or BYTE
      ShortTypeHandlerjava.lang.Short, shortAny compatible NUMERIC or SHORT INTEGER
      IntegerTypeHandlerjava.lang.Integer, intAny compatible NUMERIC or INTEGER
      LongTypeHandlerjava.lang.Long, longAny compatible NUMERIC or LONG INTEGER
      FloatTypeHandlerjava.lang.Float, floatAny compatible NUMERIC or FLOAT
      DoubleTypeHandlerjava.lang.Double, doubleAny compatible NUMERIC or DOUBLE
      BigDecimalTypeHandlerjava.math.BigDecimalAny compatible NUMERIC or DECIMAL
      StringTypeHandlerjava.lang.StringCHAR, VARCHAR
      ClobReaderTypeHandlerjava.io.Reader-
      ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
      NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
      NClobTypeHandlerjava.lang.StringNCLOB
      BlobInputStreamTypeHandlerjava.io.InputStream-
      ByteArrayTypeHandlerbyte[]Any compatible byte stream type
      BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
      DateTypeHandlerjava.util.DateTIMESTAMP
      DateOnlyTypeHandlerjava.util.DateDATE
      TimeOnlyTypeHandlerjava.util.DateTIME
      SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
      SqlDateTypeHandlerjava.sql.DateDATE
      SqlTimeTypeHandlerjava.sql.TimeTIME
      ObjectTypeHandlerAnyOTHER, or unspecified
      EnumTypeHandlerEnumeration TypeVARCHAR any string compatible type, as the code is stored (not index).
      EnumOrdinalTypeHandlerEnumeration TypeAny compatible NUMERIC or DOUBLE, as the position is stored (not the code itself).
    • mapper
      在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值