zoukankan      html  css  js  c++  java
  • 应用框架@MyBatis

    1 前言:数据库 SQL命令

    • 创建数据库并指定编码
    Create database 数据库名 default character set utf8
    
    • 创建表
    Createtable 表名(
       列名 类型 约束 auto_increament comment "备注"
    );
    

    2 命名规范

    • 项目名:没有要求,不起中文
    • 包:公司域名倒写 com
    • 数据访问层:dao, persist, mapper
    • 实体:entity, model, bean,javabean, pojo
    • 业 务 逻 辑 : service,biz
    • 控制器: controller, servlet,action,web
    • 过滤器: filter
    • 异常: exception
    • 监听器:listener
    • 注释:
      • 类上和方法上使用文档注释 /** */
      • 在方法里面使用/* *///
    • 类: 大驼峰
    • 方法,属性:小驼峰

    3 MVC开发模式

    • M: Model 模型;实体类和业务和 dao

    • V: view 视图 ;现在一般使用. JSP

    • C: Controller 控制器,现在使用servlet

      • 作用:视图和逻辑分离
    • MVC 适用场景:大型项目开发.

    • 项目实现步骤

      • 先设计数据库

      • 再写实体类:用于封装数据

      • 持久层DAO

    • 业务逻辑

      • 控制器

      • 视图

        MVC开发模式

    4 框架概念

    • 框架:软件的半成品.未解决问题制定的一套约束,在提供功能基础上进行扩充。
    • 框架中一些不能被封装的代码(变量),需要使用框架者新建一个 xml 文件,在文件中添加变量内容
      • 需要建立特定位置和特定名称的配置文件
      • 需要使用 xml 解析技术和反射技术
      • 是 MySQL Mapper Framework for Java

    常用概念

    • 类库:提供的类没有封装一定逻辑。
      举例:类库就是名言警句,写作文时引入名言警句

    • 框架:区别于类库,里面有一些约束。
      举例:框架是填空题

    5 MyBatis 简介

    • Mybatis 开源免费框架。原名叫 iBatis,2010 在 google code,2013 年迁移到 github

    • 作用: 数据访问层(即是 Dao)框架.

      • 底层是对 JDBC 的封装.
    • mybatis 优点之一:

      • 使用 mybatis 时不需要编写实现类,只需要写需要执行的 sql 命令。

    6 MyBatis 使用

    (一)环境搭建

    • 步骤一:导入 jar
    包名 作用
    mybatis.jar MyBatis 的核心包
    mysql-connector-java.jar MySQL 驱动包
    cglib.jar 动态代理包
    asm.jar cglib 依赖包
    javassist-GA.jar cglib 依赖包(负责字节码解析的包)
    commons-logging.jar 日志包
    log4j.jar 日志包
    log4j-api.jar 日志包
    log4j-core.jar 日志包
    slf4j-api.jar 日志包
    slf4j-log4j.jar 日志包
    • 步骤二:在 src 下新建全局配置文件(编写 JDBC 四个变量)
      • 没有名称和地址要求
      • 在全局配置文件中引入 DTD 或 schema【使用见 Blogs -> DTD】
        • 如果导入 dtd 后没有提示:Window--> preference --> XML --> XMl catalog --> add 按钮
      • 全局配置文件内容【MyBatis.xml】
    <?xml version="1.0" encoding="UTF-8"?>
    <!--注意这里是 configuration ,下面为 mybatis-3-config -->
    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">
      
    <configuration>
      <!-- default 为引用 environment 的 id,即当前所使用的环境 -->
      <environments default="default">
      	<!-- 声明可以使用的环境 -->
      	<environment id="default">
      		<!-- 使用原生 JDBC 事务 -->
      		<transactionManager type="JDBC"></transactionManager>
      		<dataSource type="POOLED">
          		  <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
          	          <property name="url" value="jdbc:mysql://localhost:3306/数据库名"/>
          		  <property name="username" value="root"/>
          	          <property name="password" value="GJXAIOU"/>
      		</dataSource>
      	</environment>
      </environments>
      
      <mappers>
          <!-- 配置实体类 Mapper 文件,一般使用 package -->
      	<mapper resource="com/mapper/FlowerMapper.xml"/>
      </mappers>
    </configuration>
    

    注: mybatis.xml 中的 <mappers>中配置的 resource 是整个项目中的所有其他 实体类Mapper.xml 文件;

    mappers 标签下有许多 mapper 标签,每一个 mapper 标签中配置的都是一个独立的映射配置文件的路径,配置方式有以下几种。

    第一种:使用相对路径进行配置。示例代码如下:

    <mappers>
        <mapper resource="org/mybatis/mappers/UserMapper.xml"/>
        <mapper resource="org/mybatis/mappers/ProductMapper.xml"/>
        <mapper resource="org/mybatis/mappers/ManagerMapper.xml"/>
    </mappers>
    

    第二种:使用绝对路径进行配置。示例代码如下:

    <mappers>
        <mapper url="file:///var/mappers/UserMapper.xml"/>
        <mapper url="file:///var/mappers/ProductMapper.xml"/>
        <mapper url="file:///var/mappers/ManagerMapper.xml"/>
    </mappers>
    

    第三种:使用接口信息进行配置。示例代码如下:

    <mappers>
        <mapper class="org.mybatis.mappers.UserMapper"/>
        <mapper class="org.mybatis.mappers.ProductMapper"/>
        <mapper class="org.mybatis.mappers.ManagerMapper"/>
    </mappers>
    

    第四种:使用接口所在包进行配置。示例如下:

    <mappers>
        <package name="org.mybatis.mappers"/>
    </mappers>
    
    • 步骤三:新建以 mapper 结尾的包,在包下新建:实体类名+Mapper.xml
      • 文件作用:编写需要执行的 SQL 命令
      • 把 xml 文件理解成实现类

    FlowerMapper.xml 文件内容为:【注意抬头中的信息不同,将上面抬头中的 config 全部换为 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:理解成实现类的全路径(包名+类名),用于绑定 Dao 接口(即面向接口编程),使用 namespace 之后就不用写实现类,业务逻辑会直接通过这个绑定寻找到对应点的 SQL 语句进行对应的数据处理 -->
    <mapper namespace="a.b" >
    	<!-- 如果方法返回值是list,在resultType中写List的泛型,因为mybatis
    		对jdbc封装,一行一行读取数据-->
    	<!--id:表示方法名; parameterType:定义参数类型;resultType:返回值类型 -->
    	<select id="selAll" resultType="com.pojo.Flower">
    		select * from flower
    	</select>
    </mapper>
    
    • 步骤四:测试结果(只有在单独使用 mybatis 时使用,最后 ssm 整合时下面代码不需要编写
    import com.pojo.Flower;
    
    public class Test {
    	public static void main(String[] args) throws IOException {
    		InputStream is = Resources.getResourceAsStream("mybatis.xml");
    		//使用工厂设计模式
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    		//生产SqlSession
    		SqlSession session=factory.openSession();
    		
    		List<Flower> list = session.selectList("a.b.selAll");
    		for (Flower flower : list) {
    			System.out.println(flower.toString());
    		}
    		
    		session.close();
    	}
    }
    

    (二)环境搭建详解

    全局配置文件中内容

    • <transactionManager/> type 属性可取值如下:
      • JDBC,事务管理使用 JDBC 原生事务管理方式;
      • MANAGED 把事务管理转交给其他容器,相当于设置原生 JDBC 事务setAutoMapping(false)
    • <dataSouce/>type 属性可取值如下:
      • POOLED:使用数据库连接池;
      • UNPOOLED:不使用数据库连接池,和直接使用 JDBC 一样;
      • JNDI:是 java 命名目录接口技术;

    7 数据库连接池【补充知识】

    • 在内存中开辟一块空间,存放多个数据库连接对象;
    • JDBC Tomcat Pool,直接由 tomcat 产生数据库连接池;
    • 图示:数据库连接池中有很多连接,他们可能处于 Active 或者 Idle 等等状态;
      • active 活跃状态:当前连接对象被应用程序使用中
      • Idle 空闲状态:等待应用程序使用
    • 使用数据库连接池的目的
      • 在高频率访问数据库时,使用数据库连接池可以降低服务器系统压力,提升程序运行效率;
        • 小型项目不适用数据库连接池;
    • 实现 JDBC tomcat Pool 的步骤
      • 在 web 项目的 META-INF 中存放 context.xml,在 context.xml 编写数据库连接池相关属性
      • 把项目发布到 tomcat 中,则数据库连接池产生了
    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
    	<Resource
    		driverClassName="com.mysql.jdbc.Driver"
    		url="jdbc:mysql://localhost:3306/lianxi"
    		username="root"
    		password="moyue"
    		maxActive="50"
    		maxIdle="20"
    		name="test"
    		auth="Container"
    		maxWait="10000"
    		type="javax.sql.DataSource"
    	/>
    </Context>
    
    • 可以在 Java 中使用 JNDI 获取数据库连接池中对象
    • Context:上下文接口.context.xml 文件对象类型;代码见下面
    • 当关闭连接对象时,把连接对象归还给数据库连接池,把状态改变成 Idle
    @WebServlet("/pool")
    public class DemoServlet extends HttpServlet {
    	@Override
    	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
      	  try {
      		Context cxt = new InitialContext();
      		DataSource ds = (DataSource) cxt.lookup("java:comp/env/test");
      		Connection conn = ds.getConnection();
      		PreparedStatement ps = conn.prepareStatement("select * from flower");
      		ResultSet rs = ps.executeQuery();
      		res.setContentType("text/html;charset=utf-8");
      		PrintWriter out = res.getWriter();
      		while(rs.next()){
      			out.print(rs.getInt(1)+"&nbsp;&nbsp;&nbsp;&nbsp;"+rs.getString(2)+"<br/>");
      		}
      		out.flush();
      		out.close();
      		rs.close();
      	} catch (NamingException e) {
      		e.printStackTrace();
      	} catch (SQLException e) {
      		e.printStackTrace();
      	}
      }
    }
    

    8 三种查询方式

    • selectList() 返回值为 List<resultType 属性控制>
      适用于查询结果都需要遍历的需求

    • selectOne() 返回值 Object
      适用于返回结果只是变量或一行数据时

    • selectMap() 返回值 Map<key,resultType 属性控制>
      适用于需要在查询结果中通过某列的值取到这行数据的需求

    public class Test {
    	public static void main(String[] args) throws IOException {
            /** MyBatis 默认不加载配置文件,因此需要先加载配置文件,返回整个配置文件的流对象;
            * 在数据访问层处理异常和在控制器中处理异常,一般在 service 中只抛出异常;
            */
    		InputStream is = Resources.getResourceAsStream("mybatis.xml");
    		// 使用工厂设计模式
    		// 前面是工厂  实例化工厂对象时使用的是构建者设计模式   它的名称标志:后面有Builder 
    		// 构建者设计模式意义: 简化对象实例化过程
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    		// 生产 SqlSession, 整个 sqlsession 就是 MyBatis 中 API 封装的对象,增删改查都在里面
    		SqlSession session=factory.openSession();
    		
    		// selectList
    		List<Flower> list = session.selectList("a.b.selAll");
    		for (Flower flower : list) {
    			System.out.println(flower.toString());
    		}
    		
    		// selectOne
    		int count = session.selectOne("a.b.selById");
    		System.out.println(count);
    		
    		// selectMap
    		// 把数据库中哪个列的值当作 map 的 key
    		Map<Object, Object> map = session.selectMap("a.b.c", "name123");
    		System.out.println(map);
    		
    		session.close();
    	}
    }
    

    对应的 实体类 Mapper.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">
      
    <mapper namespace="a.b" >
    	<select id="selAll" resultType="com.pojo.Flower">
    		select id,name name123,price,production from flower
    	</select>
    	
    	// 这里的 int 相当于 Ingeter
    	<select id="selById" resultType="int">
    		select count(*) from flower
    	</select>
    	
    	<select id="c" resultType="com.pojo.Flower">
    		select id,name name123,price,production from flower
    	</select>
    	
    </mapper>
    

    9 注解

    • 注解存在的意义:简化 xml 文件的开发;
    • 注解在 servlet 3.0 规范之后大力推广的;
    • 注解前面的@XXX,表示引用一个 @interface;
      • @interface 表示注解声明
    • 注解可以有属性,因为注解其实就是一个接口(类);
    • 每次使用注解都需要导包
    • 注解语法: @XXXX(属性名= 值)
    • 值的分类
      • 如果值是基本数据类型或字符串: 属性名=值
      • 如果值是数组类型: 属性名={值,值}
        • 如果只有一个值可以省略大括号;
      • 如果值是类类型:属性名=@名称
    • 如果注解只需要给一个属性赋值,且这个属性是默认属性,可以省略属性名

    10 路径

    • 编写路径为了告诉编译器如何找到其他资源;

    • 路径分类

      • 相对路径: 从当前资源出发找到其他资源的过程
      • 绝对路径: 从根目录(服务器根目录或项目根目录)出发找到其他资源的过程
      • 标志: 只要以/开头的都是绝对路径
    • 绝对路径:
      • 如果是请求转发 / 表示项目根目录(WebContent)
      • 其他重定向,<img/> <script/><style/>,location.href 等/都表示服务器根目录(tomcat/webapps 文件夹)
    • 如果客户端请求的控制器,控制器转发到JSP 后,jsp 中如果使用相对路径,需要按照控制器的路径去找其他资源;
      • 保险办法:使用绝对路径,可以防止上面的问题;

    11 Log4J

    • 由 apache 推出的开源免费日志处理的类库;

    • 为什么需要日志:

      • 在项目中编写 System.out.println();输出到控制台,当项目发布到 tomcat 后,没有控制台(在命令行界面能看见.),不容易观察一些输出结果。
      • log4j 作用:不仅能把内容输出到控制台,还能把内容输出到文件中,便于观察结果。
    • 使用步骤:

      • 导入 log4j-xxx.jar
      • 在 src 下新建 log4j.properties(路径和名称都不允许改变)
        • 在 ConversionPattern 中书写表达式(就是日志输出的格式);
        • log4j.appender.LOGFILE.File 日志文件位置及名称(日志文件扩展名.log)
    // 遇到什么级别才输出以及输出位置:调试信息输出, 输出到控制台,输出到 log 文件
    log4j.rootCategory=DEBUG, CONSOLE, LOGFILE
    // 向控制台输出相关的配置
    log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%C %d{YYYY-MM-dd hh:mm:ss}%m  %n
    // 向日志文件输出的相关的配置
    log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=E:/my.log 
    log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.Patter nLayout log4j.appender.LOGFILE.layout.ConversionPattern=%C	%m %L %n
    
    • log4j 输出级别(一共五个级别)

      • fatal(致命错误) > error ( 错误) > warn ( 警告) > info(普通信息) > debug(调试信息)
      • 在 log4j.properties 的第一行中控制输出级别,只有大于等于该级别信息才输出
    • log4j 输出目的地

      • 在一行控制输出目的地,其中 CONSOLE 表示控制台 ,LOGFILE 表示文件

    在实际使用:新建一个类,例如:Test类

    • 使用方法一:
    Logger  logger = Logger.getLogger(Test.class);
    // 这里以 debug 为例,具体的方法看上面输出级别调用不同的方法
    logger.debug("这是调试信息"); 
    
    • 使用方法二:
      在 catch 中 : logger.error(e.getMessage()); 即可,注意在log4j配置文件中加上相关的表达式;

    • pattern 中常用几个表达式(区分大小写)

      • %C 输出包名+类名;
      • %d{YYYY-MM-dd HH:mm:ss} 时间;
      • %L 行号
      • %m 信息
      • %n 换行
        上面可以多个一起使用, 中间空格就行
        示例如下:

    pattern示例配置

    12 <settings>标签

    该标签必须配置在最前面

    • 在 mybatis 全局配置文件中通过<settings>标签控制 mybatis 全局开关
    • 在 mybatis.xml 中开启 log4j 命令如下:
      • 必须保证有 log4j.jar
      • 在 src 下有 log4j.properties
    <settings>
        <setting  name="logImpl"  value="LOG4J"/>
    </settings>
    
    • log4j 中可以输出指定内容的日志(控制某个局部内容的日志级别) :直接在 log4j.properties 中设置;
      • 命名级别(包级别): <mapper>namespace 属性中除了最后一个类名
        例如 namespace=”com.mapper.PeopleMapper” 其中包级别为 com.mapper ,需要在 log4j.propeties 中做两件事情
      • 先在总体级别调成 Error,这样可以不输出无用信息
      • 在设置某个指定位置级别为 DEBUG

    根据命名空间设置不同优先级

    • 类级别
      • namespace 属性值 ,相当于namespace 类名
    • 方法级别
      • 使用 namespace 属性值+标签 id 属性值

    13 parameterType 属性(Mybatis 中参数设置)

    传递多个参数时候,可以使用对象或者 map,和第二部分的多参数实现方法

    • 在 XXXMapper.xml 中<select> <delete>等标签的 parameterType 可以控制参数类型(例如可以传入 select 语句的参数,控制输入参数的类型)

    • SqlSession 的 selectList()selectOne() 的第二个参数和 selectMap() 的第三个参数都表示方法的参数

      • 示例:
    People  p  =  session.selectOne("a.b.selById",1);
    System.out.println(p);
    
    • 在 实例名Mapper.xml 中可以通过#{}获取参数(代码见下)
      • parameterType 控制参数类型
      • #{} 获取参数内容
        • 使用索引,从 0 开始 #{0}表示第一个参数(尽量不使用这个方法)
        • 也可以使用#{param1},表示第一个参数
        • 如果只有一个参数(其参数应该是基本数据类型或 String),mybatis 对 #{} 里面内容没有要求只要写内容即可。
        • 如果参数是对象#{属性名}
        • 如果参数是 map, 写成#{key} :当需要传递多个参数时候,目前只能使用 map 或者对象
    <select id="selById"
      resultType="com.pojo.People"  parameterType="int">
      select * from people where id=#{0}
    </select>
    
    • #{}${} 的 区 别
      • #{} 获取参数的内容,支持索引获取,或者使用 param1 获取指定位置参数,并且 SQL 使用?占位符
      • ${} 字符串拼接,不使用?,默认找${内容}内容的 get/set 方法,如果写数字,就是一个数字

    配置示例:
    PeopleMapper.xml

    <select id="test" resultType="com.pojo.People" parameterType="com.pojo.People"> 
      select * from people where id =  ${id}
    </select>
    

    Test.java

    People peo =new People();
    peo.setId(1);
    People p = session.selectOne("a.b.selById",people);
    
    • 如果在 xml 文件中出现 “<” , “>” ,双引号 等特殊字符时可以使用 XML 文件转义标签(XML 自身的),格式为:<![CDATA[ 内 容 ]]>

    • mybatis 中实现 mysql 分页写法

    • ? 中不允许在关键字前后进行数学运算,需要在代码中计算完成后传递到 mapper.xml 中;

    Java 中代码为:

    //显示几个
    int pageSize = 2;
    //第几页
    int pageNumber = 2;
    //如果希望传递多个参数,可以使用对象或map
    Map<String,Object> map = new HashMap<>();
    map.put("pageSize", pageSize);
    map.put("pageStart", pageSize*(pageNumber-1));
    List<People> p = session.selectList("a.b.page",map);
    

    mapper.xml 中代码为:

    <select id="page" resultType="com.pojo.People" parameterType="map">
        select * from people limit #{pageStart},#{pageSize}
    </select>
    

    14 typeAliases 别名(在mybatis.xml中进行配置,命令如下)

    别名配置必须在 <environments>前面
    一共有三类:分别是系统内置的别名,给某个类的别名,

    • 系统内置别名: 就是把类型全小写(见文档 )
    • 给某个类起别名
      • alias=”自定义”
      • 同时 mapper.xml 中 peo 引用 People 类
    <typeAliases>
        <typeAlias type="com.pojo.People" alias="peo"/>
    </typeAliases>
    

    mapper.xml 中内容为:

    <select id="page" resultType="peo" parameterType="map">
        select * from people limit #{pageStart},#{pageSize}
    </select>
    
    • 直接给某个包下所有类起别名,别名为类名,区分大小写
      • mybatis.xml 中配置
    <typeAliases>
        <package name="com.pojo" />
    </typeAliases>
    
    • mapper.xml 中通过类名引用
    <select id="page" resultType="People" parameterType="map">
        select * from people limit #{pageStart},#{pageSize}
    </select>
    

    15 MyBatis 实现新增

    • 概念复习:下面三者本质相同,角度不同;

      • 功能:从应用程序角度出发,软件具有哪些功能.
      • 业务:完成功能时的逻辑;对应 Service 中一个方法
      • 事务:从数据库角度出发,完成业务时需要执行的 SQL 集合,统称一个事务
        • 事务回滚:如果在一个事务中某个 SQL 执行事务,希望回归到事务的原点,保证数据库数据的完整性
    • 在 mybatis 中默认是关闭了 JDBC 的自动提交功能

      • 每一个 SqlSession 默认都是不自动提交事务.
      • 可以使用 session.commit() 提交事务.
      • 也可以使用 openSession(true); 自动提交底层为:.setAutoCommit(true);
    • mybatis 底层是对 JDBC 的封装.

      • JDBC 中 executeUpdate()执行新增,删除,修改的 SQL,方法的返回值 int,表示受影响的行数.
      • 应为上面原因,因此 mybatis 中 <insert> <delete> <update> 标签没有 resultType 属性,认为返回值都是 int
    • 在 openSession() 时 Mybatis 会创建 SqlSession 时同时创建一个 Transaction(事务对象),同时 autoCommit 都为 false

      • 如果出现异常,应该 session.rollback() 回滚事务.
    • 实现新增的步骤

      • 在 mapper.xml 中提供 <insert> 标签,标签没有返回值类型
      • 通过 session.insert() 调用新增方法

    mapper.xml 值为;

    <insert id="ins" parameterType="People">
    insert into people values(default,#{name},#{age})
    </insert>
    
    int index1 = session.insert("a.b.ins", p);
    if(index1>0){
       System.out.println("成功");
    }else{
      System.out.println("失败");
    }
    

    16 MyBatis 实现修改

    • 在 mapper.xml 中提供 <update> 标签
    <update id="upd" parameterType="People">
      update people set name = #{name} where id = #{id}
    </update>
    
    • 编写代码
    People peo = new People();
    peo.setId(3);
    peo.setName("王五");
    int index = session.update("a.b.upd", peo);
    if(index>0){
        System.out.println("成功");
    }else{
        System.out.println("失败");
    }
    session.commit();
    

    17 mybatis 实现删除

    • 在 mapper.xml 提供<delete> 标签
    <delete id="del" parameterType="int">
        delete from people where id = #{0}
    </delete>
    
    • 编写代码
     int  del  =  session.delete("a.b.del",3);
    
    if(del>0){
       System.out.println("成功");
    }else{
       System.out.println("失败");
    }
    
    session.commit();
    

    18 MyBatis 接口绑定方案及多参数传递

    (一)接口绑定

    • 作用: 实现创建一个接口后把 mapper.xml 由mybatis 生成接口的实现类,通过调用接口对象就可以获取 mapper.xml 中编写的 sql。后面 mybatis 和 spring 整合时使用的是这个方案.

    • 实现步骤:

      • 创建一个接口 :例如:LogMapper
        • 接口 Mapper 名和接口名与 mapper.xml 中
          <mapper>namespace 相同

          目录示例:com.mapper包下面包含一个接口:LogMapperLogMapper.xml,然后 LogMapper.xml 中的<mapper>namespace 标签格式为:
          <mapper namespace = "com.mapper.LogMapper> </mapper>
        • 接口中方法名和 mapper.xml 标签的 id 属性相同;
      • 在 mybatis.xml 中使用<package>进行扫描接口和 mapper.xml;
    • 代码实现步骤:

      • 首先在 mybatis.xml 中全局配置文件中的<mappers>下使用<package>
    <mappers>
         <package name="com.mapper"/>
    </mappers>
    
    • 然后在 com.mapper 包下新建接口:LogMapper
    public  interface  LogMapper  { 
        List<Log>  selAll();
    }
    
    • 然后在 com.mapper 中新建一个 LogMapper.xml
      其中 namespace 的值必须和接口全限定路径(包名+类名)一致,且使用的 id 值必须和接口中方法名相同。同时如果接口中方法为多个参数,可以省略 parameterType
    <mapper  namespace="com.mapper.LogMapper">
      <select  id="selAll"  resultType="log"> 
          select  *  from  log
      </select>
    </mapper>
    

    绑定的使用:在其它 Java 类中

    public class Test {
    	public static void main(String[] args) throws IOException {
    		InputStream is = Resources.getResourceAsStream("mybatis.xml");
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    		SqlSession session = factory.openSession();		
    
    		LogMapper logMapper = session.getMapper(LogMapper.class);
    		List<Log> list = logMapper.selAll();
    		for (Log log : list) {
    			System.out.println(log);
    		}
    

    (二)多参数实现办法

    一共有两种方法:一般方法和使用注解

    • 方法一:一般方法
      首先在在接口 LogMapper 中声明方法
    List<Log>  selByAccInAccout(String accin, String accout);
    

    然后在 LogMapper.xml 中添加即可,#{}中可以使用 param1,param2

    <select id="selByAccInAccout" resultType="log">
         select * from log where accin=#{param1} and accout=#{param2}
    </select>
    
    • 方法二:可以使用注解方式
      首先在接口中声明方法
    /**
    mybatis 把参数转换为 map 了,其中@Param("key")  参数内容就是 map 的 value
    */
    
    List<Log> selByAccInAccout(@Param("accin")  String accin123,@Param("accout")  String  accout3454235);
    

    然后在 mapper.xml 中添加

    <!--  当多参数时,不需要写 parameterType  -->
    <select  id="selByAccInAccout"  resultType="log"  > 
        select * from log where accin=#{accin} and accout=#{accout}
    </select>
    

    注:#{} 里面写@Param(“内容”)参数中内容。

    多参数的使用
    当然 log 是存在数据库中的一个表中,同时要新建实体类的;
    在其它 Java 类中使用:

    public class Test {
       public static void main(String[] args) throws IOException {
          InputStream is = Resources.getResourceAsStream("mybatis.xml");
          SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
          SqlSession session = factory.openSession();
    
          LogMapper logMapper = session.getMapper(LogMapper.class);
          List<Log> list = logMapper.selByAccInAccout("3", "1");
          for (Log log : list) {
              System.out.println(log);
          }
    
          session.close();
          System.out.println("程序执行结束");
        }
    }
    

    19 动态 SQL

    1. 根据不同的条件需要执行不同的 SQL 命令,称为动态 SQL
    2. MyBatis 中动态 SQL 的实现是在 实体类mapper.xml 中添加逻辑判断即可
      注:以下的 xml 配置均在 LogMapper.xml 中;

    (一)If 使用

    <select  id="selByAccinAccout"  resultType="log"> 
          select  *  from  log  where  1=1
    <!--  OGNL 表达式,直接写 key 或对象的属性.不需要添加任何特字符号,这里的 and 执行的时候都会被转换为 & -->
          <if  test = "accin != null and accin != ''"> 
              and  accin = #{accin}
          </if>
    
          <if  test= "accout != null and accout != ''"> 
              and  accout=#{accout}
          </if>
    </select>
    

    (二)where 使用

    • 当编写 where 标签时,如果内容中第一个是 and 就会去掉第一个 and;

    • 如果 <where> 中有内容会生成 where 关键字,如果没有内容不生成 where 关键字;

    • 使用示例 :效果:比直接使用 <if> 少写了 where 1 = 1;

    <select  id="selByAccinAccout"  resultType="log"> 
        select  *  from  log
      <where>
         <if  test="accin!=null  and  accin!=''"> 
             and  accin=#{accin}
         </if>
      
         <if  test="accout!=null  and  accout!=''"> 
             and  accout=#{accout}
         </if>
      </where>
    </select>
    

    (三)choose、when、otherwise 使用

    • 只要有一个成立,其他都不执行

    • 代码示例
      如果 accin 和 accout 都不是 null 或不是””生成的 sql 中只有 where accin=?

    <select  id = "selByAccinAccout"  resultType = "log"> 
         select * from log
         <where>
            <choose>
                <when test = "accin != null and accin != ''">
                       and accin = #{accin}
                </when>
    
                <when test = "accout != null and accout != ''"> 
                       and accout = #{accout}
                </when>
           </choose>
        </where>
    </select>
    

    (四)set 使用

    • 作用:去掉最后一个逗号
      <set>用在修改 SQL 中 set 从句,如果<set>里面有内容则生成 set 关键字,没有就不生成

    • 示例
      其中:id=#{id} 目的防止<set>中没有内容,mybatis 不生成 set 关键字,如果修改中没有 set 从句 SQL 语法错误.

    <update  id="upd"  parameterType="log"  > 
        update  log
        <set>
            id=#{id},
            <if  test="accIn!=null  and  accIn!=''"> 
                accin=#{accIn},
            </if>
    
            <if  test="accOut!=null  and  accOut!=''">
                 accout=#{accOut},
            </if>
        </set>
        where id=#{id}
    </update>
    

    (四) Trim 使用

    里面的主要方法如下:

    • prefix 在前面添加内容

    • prefixOverrides 去掉前面内容

    • suffix 在后面添加内容

    • suffixOverrieds 去掉后面内容

    • 执行顺序:首先去掉内容然后添加内容;
      代码示例

    <update  id = "upd"  parameterType = "log"> 
        update  log
        <trim prefix = "set" suffixOverrides = ","> 
            a = a,
        </trim> 
        where  id=100
    </update>
    

    (五)bind 使用

    • 作用:给参数重新赋值
    • 使用场景:
      • 模糊查询:就是在 SQL 语句中,将用户输入的数据前后加上 % 然后执行语句;见示例
      • 在原内容前或后添加内容 :将用户输入的数据进行格式化之后存入数据库;
    <select  id="selByLog"  parameterType="log" resultType="log">
        <bind  name="accin"  value="'%'+accin+'%'"/> 
            #{money}
    </select>
    
    

    (六)foreach标签使用

    • 用于循环参数内容,还具备在内容的前后添加内容,以及添加分隔符功能

    • 适用场景:主要用于 in 查询以及批量新增中(但是 mybatis 中 foreach 效率比较低)

    批量新增操作:

    • 默认的批量新增的 SQL 语句为:insert into log VALUES (default,1,2,3),(default,2,3,4),(default,3,4,5)

    • 在执行批处理的时候,需要将 openSession()必须指定下面命令
      factory.openSession(ExecutorType.BATCH);,这里底层 是 JDBC 的 PreparedStatement.addBatch();

    • foreach示例

      • collection=”” 要遍历的集合
      • item 迭代变量, 可以使用#{迭代变量名}获取内容
      • open 循环后左侧添加的内容
      • close 循环后右侧添加的内容
      • separator 每次循环时,元素之间的分隔符
    <select  id="selIn"  parameterType="list" resultType="log">
        select  *  from  log  where  id  in
        <foreach  collection="list"  item="abc"  open="(" close=")" separator=",">
            #{abc}
         </foreach>
    </select>
    

    (七)<sql><include> 搭配使用

    • 某些 SQL 片段如果希望复用,可以使用 <sql> 定义这个片段
    <sql  id="mysql"> 
        id,accin,accout,money
    </sql>
    
    • 可以在 <select><delete><update><insert> 中使用 <include> 引用
    <select  id="">
        select  <include  refid="mysql"></include>
        from  log
    </select>
    

    20 ThreadLocal

    主要优化 service中的方法

    • 线程容器:给线程绑定一个 Object 内容后只要线程不变,可以随时取出。但是一旦改变线程,无法取出内容。

    • 语法示例

    final ThreadLocal<String> threadLocal = ThreadLocal<>();
        threadLocal.set("测试");
        new Thread(){
            public  void  run()  {
                String  result  =  threadLocal.get();
                System.out.println("结果:"+result);
            };
        }.start();
    

    21 缓存

    1. 应用程序和数据库交互的过程是一个相对比较耗时的过程;
    2. 缓存存在的意义:让应用程序减少对数据库的访问,提升程序运行效率;
    • MyBatis 中默认 SqlSession 缓存开启

      • 同一个 SqlSession 对象调用同一个 <select> 时,只有第一次访问数据库,第一次之后把查询结果缓存到 SqlSession 缓存区(内存)中
      • 缓存的是 statement 对象.(简单记忆必须是用一个<select>)
        • 在 myabtis 时一个 <select> 对应一个 statement 对象
      • 有效范围必须是同一个 SqlSession 对象
    • 缓存流程

      • 步骤一:先去缓存区中找是否存在 statement
      • 步骤二:如果存在返回结果
      • 步骤三:如果没有缓存 statement 对象,去数据库获取数据
      • 步骤四:数据库返回查询结果
      • 步骤五:把查询结果放到对应的缓存区中

    缓存

    • SqlSessionFactory 缓存(二级缓存)
      • 有效范围:同一个 factory 内,哪个 SqlSession 都可以获取
      • 当数据频繁被使用,很少被修改的使用使用二级缓存
      • 使用二级缓存步骤
        • 在 mapper.xml 中添加
        • 如果不写 readOnly=”true”需要把实体类序列化;
          <cache readOnly = "true"></cache>
      • 当 SqlSession 对象 close()时或 commit() 时会把 SqlSession 缓存的数据刷 (flush) 到 SqlSessionFactory 缓存区中

    22 MyBatis 实现多表查询

    (一)Mybatis 实现多表查询方式

    一共三种方式

    • 业务装配:对两个表编写单表查询语句,在业务(Service)把查询的两个结果进行关联;
    • 使用Auto Mapping 特性,在实现两表联合查询时通过别名完成映射;
    • 使用 MyBatis 的 <resultMap> 标签进行实现;

    (二)多表查询时,类中包含另一个类的对象的分类

    • 单个对象
    • 集合对象

    23 resultMap 标签

    相当于现在直接设置映射关系,进行两者(SQL 查询结果和实体类之间)的匹配。

    • <resultMap> 标签写在 实体类Mapper.xml 中,由程序员控制SQL 查询结果与实体类的映射关系;

    • 默认 MyBatis 使用 Auto Mapping 特性进行映射:即保持查询的数据库中的列名和实体类中的属性名相同即可;

    • 使用 <resultMap>标签时,<select>标签不写 resultType 属性,而是使用 resultMap 属性来引用<resultMap>标签.

    (一)使用 resultMap 实现单表映射关系

    • 首先进行数据库设计
      示例:数据库表 teacher 中两个字段: id、name

    • 然后进行实体类设计

    public class Teacher{
       private int id1;
       private String name1;
    }
    
    • 然后实现 TeacherMapper.xml 代码
    <!-- 其中 type 的值为返回值类型-->
    <resultMap type="teacher" id="mymap">
        <!-- 主键使用id 标签配置映射关系-->
        <!-- column 值为数据库中列名; property 值为实体类中的属性名 -->
        <id column="id" property="id1" />
        
        <!-- 其他列使用result 标签配置映射关系-->
        <result column="name" property="name1"/>
    </resultMap>
    
    <select id="selAll" resultMap="mymap">
          select * from teacher
    </select>
    

    上面代码如果使用原来的数据库中字段的名称和实体类相同的话,代码如下:

    <select id = "selAll" resultType = "teacher">
        select * from teacher
    </select>
    

    (二)使用 resultMap 实现关联单个对象(N+1 方式)

    • N+1 查询方式:先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息;
    • N+1 查询方式与业务装配的区别:
      原来在 service 里面写的代码,现在由 mybatis 完成装配;

    实现步骤:

    • 首先在 Student 实现类中包含了一个 Teacher 对象
    public class Student {
        private int id;
        private String name;
        private int age;
        private int tid;
        private Teacher teacher;
        // 该 POJO 类中其他信息省略
    }
    
    • 然后在 TeacherMapper.xml 中提供一个查询
    <select id="selById" resultType="teacher" parameterType="int">
          select * from teacher where id=#{0}
    </select>
    
    • 最后在下面的 StudentMapper.xml 中完成装配

    • <association> 表示当装配一个对象时使用

    • property: 是对象在类中的属性名

    • select:通过哪个查询查询出这个对象的信息

    • column: 把当前表的哪个列的值做为参数传递给另一个查询

    • 大前提使用 N+1 方式时:如果列名和属性名相同可以不配置,使用 Auto mapping 特性.但是 mybatis 默认只会给列装配一次

    <resultMap type="student" id="stuMap">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="tid" column="tid"/>
    
    <!-- 如果关联一个对象,使用 association 标签,调用 teacher 中的查询,如果关联多个对象,使用 collection 标签 -->
    // 老师查询中需要一个 Int 类型的参数,这里要通过 column 告诉他传入哪一列的值
        <association property="teacher" select="com.mapper.TeacherMapper.selById"  column="tid">
        </association>
    </resultMap>
    
    <select id="selAll" resultMap="stuMap">
          select * from student
    </select>
    
    • 因为这里属性名和字段名相同,因此可以把上面代码简化成
    <resultMap type="student" id="stuMap">
        <result column="tid" property="tid"/>
        <!-- 如果关联一个对象-->
        <association property="teacher"
    select="com.mapper.TeacherMapper.selById"
    column="tid"></association>
    </resultMap>
    
    <select id="selAll" resultMap="stuMap">
          select * from student
    </select>
    

    (三)使用 resultMap 实现关联单个对象(联合查询方式)

    • 实现只需要编写一个 SQL,在 StudentMapper.xml 中添加下面效果
      • <association/> 只要装配 一个对象就用这个标签
      • 此时把 <association/> 小的看待
      • javaType 属性:装配完后返回一个什么类型的对象.取值是一个类(或类的别名)
    <resultMap type="Student" id="stuMap1">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="age" property="age"/>
        <result column="tid" property="tid"/>
        
        <association property="teacher" javaType="Teacher" >
        <id column="tid" property="id"/>
        <result column="tname" property="name"/>
        </association>
    </resultMap>
    
    <select id="selAll1" resultMap="stuMap1">
        select s.id sid,s.name sname,age age,t.id tid,t.name tname FROM student s left outer join teacher t on s.tid=t.id
    </select>
    

    (四)N+1 方式和联合查询方式对比

    • N+1:适用于需求不确定时;
    • 联合查询:需求中确定查询时两个表一定都查询;

    (五)N+1 名称由来

    • 举例:学生中有 3 条数据

    • 需求:查询所有学生信息及授课老师信息

    • 需要执行的 SQL 命令

      • 查询全部学生信息:select * from 学生
      • 执行 3 遍: select * from 老师 where id=学生的外键
    • 使用多条 SQL 命令查询两表数据时,如果希望把需要的数据都查询出来,需要执行 N+1 条 SQL 才能把所有数据库查询出来;

    • 缺点:效率低

    • 优点:如果有的时候不需要查询学生是同时查询老师,只需要执行一个 select * from student;

    • 适用场景: 有的时候需要查询学生同时查询老师,有的时候只需要查询学生.

    • 如果解决 N+1 查询带来的效率低的问题

      • 默认带的前提: 每次都是两个都查询;
      • 使用两表联合查询;

    (六)使用 <resultMap> 查询关联集合对象(N+1 方式)

    实现查询老师的时候,把关联的学生也查询出来
    以为老师和学生是一对多的关系,因此查询结果是一个集合;

    • 首先在 Teacher 实体类中添加 List
    public class Teacher {
        private int id;
        private String name;
        private List<Student> list;
        // 省略其他 get、set 方法
    }
    
    • 然后在 StudentMapper.xml 中添加通过 tid 查询
    <select id="selByTid" parameterType="int" resultType="student">
          select * from student where tid=#{0}
    </select>
    
    • 然后在 TeacherMapper.xml 中添加查询全部
      其中 <collection/> 是当属性是集合类型时使用的标签;
    <resultMap type="teacher" id="mymap">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
    
        <collection property="list" select="com.mapper.StudentMapper.selByTid" column="id">
        </collection>    
    </resultMap>
    
    <select id="selAll" resultMap="mymap">
          select * from teacher
    </select>
    

    (七)使用 <resultMap> 实现加载集合数据(联合查询方式)

    • 首先在 teacherMapper.xml 中添加
      mybatis 可以通过主键判断对象是否被加载过,因此不需要担心创建重复 Teacher
    <resultMap type="teacher" id="mymap1">
        <id column="tid" property="id"/>
        <result column="tname" property="name"/>
        <collection property="list" ofType="student" >
            <id column="sid" property="id"/>
            <result column="sname" property="name"/>
            <result column="age" property="age"/>
            <result column="tid" property="tid"/>
        </collection>
    </resultMap>
    
    <select id="selAll1" resultMap="mymap1">
        select t.id tid,t.name tname,s.id sid,s.name sname,age,tid from teacher t LEFT JOIN student s on t.id=s.tid;
    </select>
    

    24 使用 Auto Mapping 结合别名实现多表查询

    只能查询对象,查询结合只能使用上面的方法

    • 只能使用多表联合查询方式,不能使用 N+1 方式
    • 要求:查询出的列名和属性名相同.

    实现方式

    • 因为.在 SQL 是关键字符,因此两侧添加反单引号;
    <select id="selAll" resultType="student">
        select t.id `teacher.id`, t.name `teacher.name`, s.id id, s.name name, age, tid from student s LEFT JOIN teacher t on t.id=s.tid
    </select>
    

    25 MyBatis 注解

    注解是写在类中的,不是XML中的,相当于将 实体类Mapper.xml 中的内容直接在对应的接口中实现即可,不在需要 实体类Mapper.xml 文件。

    • 注解:为了简化配置文件;

    • Mybatis 的注解简化 实体类Mapper.xml 文件;

      • 如果涉及动态 SQL 依然使用 mapper.xml
    • mapper.xml 和注解可以共存.

    • 使用注解时要在 mybatis.xml 中标签中使用下面两种方式之一进行配置:

      • <package/>
      • <mapper class=””/>

    可以在测试的时候使用下面:因为是接口:

    TeacherMapper  tm = session.getMapper(TeachterMapper.class)
    List<Teacher> = tm.selAll(info)
    

    具体的使用方式

    下面语句直接在 TeacherMapper.java 接口中书写即可

    • 实现查询
    @Select("select * from teacher")
    List<Teacher> selAll();
    
    • 实现新增
    @Insert("insert into teacher values(default,#{name})")
    int insTeacher(Teacher teacher);
    
    • 实现修改
    @Update("update teacher set name=#{name} where id=#{id}")
    int updTeacher(Teacher teacher);
    
    • 实现删除
    @Delete("delete from teacher where id=#{0}")
    int delById(int id);
    
    • 使用注解实现功能
      以 N+1 举例

      • 首先在 StudentMapper 接口添加查询
    @Select("select * from student where tid=#{0}")
    List<Student> selByTid(int tid);
    
    • 然后在 TeacherMapper 接口添加下面代码
    • @Results() 相当于
    • @Result() 相当于
      - @Result(id=true) 相当与
    • @Many() 相当于
    • @One() 相当于
        @Results(value={
            @Result(id=true,property="id",column="id"),
            @Result(property="name",column="name"),
            @Result(property="list",column="id",many=@Many(select="com.mapper.StudentMapper.selByTid"))
        })
        @Select("select * from teacher")
        List<Teacher> selTeacher();
    

    26 运行原理

    (一)MyBatis 运行过程中涉及到的类

    • Resources: MyBatis 中 IO 流的工具类
      • 作用是:加载配置文件
    • SqlSessionFactoryBuilder() : 构建器
      • 作用:创建 SqlSessionFactory 接口的实现类
    • XMLConfigBuilder : MyBatis 全局配置文件内容构建器类
      • 作用:负责读取流内容并转换为 JAVA 代码
    • Configuration: 封装了全局配置文件所有配置信息
      • 全局配置文件内容存放在 Configuration 中
    • DefaultSqlSessionFactory : 是SqlSessionFactory 接口的实现类
    • Transaction : 事务类
      • 每一个 SqlSession 会带有一个 Transaction 对象.
    • TransactionFactory: 事务工厂
      • 负责生产 Transaction
    • Executor : MyBatis 执行器
      • 作用:负责执行 SQL 命令
      • 相当于 JDBC 中 statement 对象(或 PreparedStatement 或 CallableStatement)
      • 默认的执行器 SimpleExcutor
      • 批量操作 BatchExcutor
      • 通过 openSession(参数控制)
    • DefaultSqlSession :是 SqlSession 接口的实现类
    • ExceptionFactory : MyBatis 中异常工厂

    (二)流程图

    MyBatis执行流程图

    (三)文字解释

    在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.下面需要实例化 SqlSessionFactoryBuilder 构建器.帮助 SqlSessionFactory 接口实现类 DefaultSqlSessionFactory.

    在实例化 DefaultSqlSessionFactory 之前需要先创建 XmlConfigBuilder 解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把Configuratin 传递给 DefaultSqlSessionFactory.到此 SqlSessionFactory 工厂创建成功.

    由 SqlSessionFactory 工厂创建 SqlSession.

    每次创建 SqlSession 时,都需要由 TransactionFactory 创建 Transaction 对象, 同时还需要创建 SqlSession 的执行器 Excutor, 最后实例化DefaultSqlSession,传递给 SqlSession 接口.

    根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作. 如果事务执行失败,需要进行 rollback 回滚事务.

    如果事务执行成功提交给数据库.关闭 SqlSession

    到此就是 MyBatis 的运行原理.

  • 相关阅读:
    Android ViewPager滑动导航菜单
    JQ操作select项
    radio控件name相同选出等于指定的value然后选中
    JQ随笔
    文本框宽度自动适应文本宽度<
    FindControl 找控件
    验证字母不符合替换成空
    JQ获取当前触发事件控件ID
    Repeater绑定对应行的dropdownlist
    有向图强连通分量的Tarjan算法
  • 原文地址:https://www.cnblogs.com/qq438649499/p/12128221.html
Copyright © 2011-2022 走看看