MyBatis学习二

1.   MyBatis 介绍

  • 1.  MyBatis SQL参数传递(掌握)
  • 2.  (多对一)关联映射(掌握)
  • 3. (一对多,多对多)集合映射(掌握)
  • 4.  SQL映射器Mapper(掌握)
  • 5. SSM集成(掌握)
  1. MyBatis SQL参数传递

#{OGNL表达式} 和 ${OGNL表达式} 的区别

2.1.  #{OGNL表达式}

MyBatis会把这个表达式使用?(占位符)替换,作为一个sql参数使用

     比如name的值为: 

     定义SQL: select * from t_user where name = #{name}

     最终SQL: select * from t_user where name = ?

2.2.  ${OGNL表达式}

MyBatis会把这个表达式的值替换到sql中,作为sql的组成部分;

该方式主要用于程序拼接SQL;

     比如name的值为:  '1  or 1=1'

     定义SQL: select * from t_user where name = ${id}

     最终SQL: select * from t_user where name = 1 or 1=1 出现sql注入

 

如果sql中使用${OGNL},并且参数的类型是(integer,string....)那么OGNL表达式可以写成任意东西;

2.3.  Sql注入

预防sql注入,使用占位符查询

select * from employee where username=? and password=?

select * from employee where username='admin' and password='admin'

字符串拼接,出现sql注入

select * from employee where username='admin' and password='0000' or '1'='1'

2.4.  ${OGNL}表达式的应用场景:不能用在登录场景,会出现sql注入

用在order by 的场景

Map<String,Object> 

map.put("orderBy",'name desc');

map.put("begin",0);

map.put("end",10);

 

在jdbc的sql代码order by后面不能使用占位符?,只能进行字符串的拼接

2.5.  复杂参数传递  

 

  1. (多对一)关联映射

3.1.  导入sql文件

 

 

CREATE TABLE `t_dept` (

 `id` bigint(20) NOT NULL AUTO_INCREMENT,

 `name` varchar(255) DEFAULT NULL,

 PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

CREATE TABLE `t_user` (

 `id` bigint(20) NOT NULL AUTO_INCREMENT,

 `name` varchar(255) DEFAULT NULL,

 `password` varchar(255) DEFAULT NULL,

 `dept_id` bigint(20) DEFAULT NULL,

 PRIMARY KEY (`id`),

 KEY `fk1` (`dept_id`),

 CONSTRAINT `fk1` FOREIGN KEY (`dept_id`) REFERENCES `t_dept` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.2.  建立Dept、User的模型

 

3.3.  配置文件MyBatis-Config.xml

<!-- 单向多对一 -->

<mapper resource="cn/itsource/mybatis/day2/_1_manytoone/DomainMapper.xml" />

3.4.  映射文件DomainMapper.xml

<mapper namespace="cn.itsource.mybatis.day2._1_manytoone.DomainMapper">

 

         <!-- 保存部门 -->

         <insert id="saveDept" parameterType="cn.itsource.mybatis.day2._1_manytoone.Dept" useGeneratedKeys="true"

                  keyProperty="id">

                  insert into t_dept(name) values(#{name})

         </insert>

         <!-- 保存用户 -->

         <insert id="saveUser" parameterType="cn.itsource.mybatis.day2._1_manytoone.User" useGeneratedKeys="true"

                  keyProperty="id">

                  insert into t_user(name,password,dept_id) values(#{name},#{password},#{dept.id})

         </insert>

 

</mapper>

3.5.  保存测试数据

String NAME_SPACE = "cn.itsource.mybatis.day2._1_manytoone.DomainMapper";

 

@Test

public void testSave() {

         SqlSession session = MyBatisUtils.getSession();

         // 先保存部门

         Dept dept1 = new Dept();

         dept1.setName("部门1");

         Dept dept2 = new Dept();

         dept2.setName("部门2");

         session.insert(NAME_SPACE + ".saveDept", dept1);

         session.insert(NAME_SPACE + ".saveDept", dept2);

 

         // 在保存用户

         for (int i = 1; i < 11; i++) {

                  User user = new User();

                  user.setName("user" + i);

                  user.setPassword("admin");

                  if (i % 2 == 0) {

                           user.setDept(dept1); // insert into t_user(dept_id) values(#{dept.id})

                  } else {

                           user.setDept(dept2);

                  }

                  session.insert(NAME_SPACE + ".saveUser", user);

         }

 

         session.commit();

         session.close();

}

3.6.  2个表关联查询的方式

3.6.1.   单表查询,不能查询部门的名称

select * from t_user

3.6.2.   等值连接

select u.*,d.*

from t_user u,t_dept d

where u.dept_id=d.id

3.6.3.   内连接

select u.*,d.*

from t_user u

join t_dept d

on u.dept_id=d.id

3.6.4.   左外连接

select u.*,d.*

from t_user u

left join t_dept d

on u.dept_id=d.id

3.7.  MyBatis提供两种方式处理我们关联对象,嵌套查询和嵌套结果。

 

3.8.  测试getAll方法

@Test

public void testGetAll() {

         SqlSession session = MyBatisUtils.getSession();

         List<User> list = session.selectList(NAME_SPACE + ".getAll");

         for (User user : list) {

                  System.out.println(user);

                  System.out.println(user.getDept());

         }

         session.close();

}

3.9.  嵌套结果(发一条左外连接sql解决问题,映射文件Mapper结果的手动封装ResultMap)

使用嵌套结果映射来处理重复的联合结果的子集。--这种方式,所有属性都要自己来!!

 

<!-- 嵌套结果(发一条左外连接sql解决问题,映射文件Mapper结果的手动封装ResultMap) -->

<select id="getAll" resultMap="userResultMap">

         select u.id,u.name,u.password,d.id did,d.name dname

         from t_user u

         left join t_dept d

         on u.dept_id=d.id

</select>

 

<resultMap type="cn.itsource.mybatis.day2._1_manytoone.User" id="userResultMap">

         <!-- 多方User的id -->

         <id property="id" column="id" />

         <!-- 多方User的属性 -->

         <result property="name" column="name" />

         <result property="password" column="password" />

         <!-- 一方Dept的id和属性 -->

         <association property="dept" javaType="cn.itsource.mybatis.day2._1_manytoone.Dept">

                  <!--一方Dept的id -->

                  <id property="id" column="did" />

                  <!--一方Dept的属性 -->

                  <result property="name" column="dname" />

         </association>

</resultMap>

3.10.         嵌套查询(发1查询user+N查询dept条sql解决问题,映射文件Mapper结果的自动封装ResultMap)

通过执行另外一个SQL映射语句来返回预期的复杂类型。

    <association ...>

<association ...>

 

select * from t_department where id = #{id}

 

 

  1. (一对多,多对多)集合映射

4.1.  常见的关系

员工和部门:

   在部门方,需要查询到当前部门下的所有员工。----集合查询

 

一个部门  拥有   多个员工

 

学生和课程

多  对  多

 

一个学生   可以学习    多门课程

一门课程   可以有      多个学生

4.2.  collection

<collection property="集合数据" ofType="集合中元素类型">

  

</collection>

 

4.3.  嵌套结果(一条sql)

嵌套查询在crm项目里面再讲

<mapper namespace="cn.itsource.mybatis.day2._3_onetomany.DomainMapper">

         <!-- 嵌套结果(发一条左外连接sql解决问题,映射文件Mapper结果的手动封装ResultMap) -->

         <select id="getAll" resultMap="deptResultMap">

                  select d.id,d.name,u.id uid,u.name uname,u.password

                  from t_dept d

                  left join t_user u

                  on d.id=u.dept_id

         </select>

 

         <resultMap type="cn.itsource.mybatis.day2._3_onetomany.Dept" id="deptResultMap">

                  <!-- 处理一方 -->

                  <id property="id" column="id" />

                  <result property="name" column="name" />

                  <!-- 处理多方 -->

                  <!-- // 单向一对多 -->

                  <!-- private Set<User> users = new HashSet<User>(); -->

                  <collection property="users" javaType="cn.itsource.mybatis.day2._3_onetomany.User">

                           <id property="id" column="uid" />

                           <result property="name" column="uname" />

                           <result property="password" column="password" />

                  </collection>

         </resultMap>

</mapper>

  1. 嵌套结果和嵌套查询的使用场景

5.1.  嵌套结果:一条sql

用在多对一的表结构

多个数据字典明细和一个数据字典类型

多个员工属于一个部门

多个部门有一个部门经理

5.2.  嵌套查询:1+n条sql

用在一对多或者多对多的表结构

展示数据的时候,在一行第一列显示一方,第二列显示多个多方,

一对多:

部门1   员工1。。。员工n

多对多:

员工1   角色1。。。角色n

  1. SQL映射器Mapper

MyBatis基于代理机制,可以让我们无需再编写Dao的实现。

6.1.  传统Dao接口,现在名称统一以Mapper结尾

 

6.2.  接口实现方式一(传统)

 

6.3.  接口实现方式二(映射器):

 

6.4.  实现步骤

6.4.1.   根据需求,创建模型相关的Mapper接口(UserMapper)

6.4.2.   编写映射文件

  1. Mapper。Xml的命名空间,必须和接口的“全限定类名”一致
  2. 定义sql标签的id,需要和“接口的方法”一致,参数一致,返回值一致
  3. 缓存

7.1.  默认支持一级缓存,二级缓存需要配置,同JPA一致

缓存是一种典型的“以空间换时间”的策略。

7.2.  一级缓存

    SqlSession级别缓存,缓存对象存储周期为第一次获取,到sqlsession被销毁掉,或是sqlSession().clearCache();

7.3.  二级缓存

    SqlSessionFactory级别缓存,缓存对象存储周期为第一次获取,到SqlSessionFactory被销毁掉(应用停止了);

默认情况下,只开启一级缓存,如果需要开启二级缓存我们需要在Mapper.xml添加一个<cache>标签和在主配置文件中<setting name="cacheEnabled" value="true"/>;

  1.    注意:需要缓存的对象,应该实现java.io.Serializable;

   

  1. SSM集成

如果要做三大框架集成,我们先保证在一个项目,每一个框架都能够独立运行!!

8.1.  集成SpringMvc

8.1.1.   导入21个jar文件(包含spring的jar)

aop,tx,jdbc,web,webmvc

dbcp,fileupload,jackson

 

8.1.2.   applicationContext.xml

Spring使用一个配置,SpringMVC使用另一个配置

1个,组件扫描,<context:component-scan base-package="cn.itsource.ssm" />

<import resource="classpath:applicationContext-mvc.xml" />

8.1.3.   applicationContext-mvc.xml  4个

mvc静态资源放行,mvc注解支持

视图解析器,上传解析器

<!-- 开启spring对springmvc的注解支持 -->

<mvc:annotation-driven />

 

<!-- 对于静态资源(图片,css,js)进行放行 -->

<mvc:default-servlet-handler />

 

<!-- 设置视图路径的前后缀,该配置可以让我们写视图路径的时候更简单。 -->

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

         <!--前缀: jsp在当前工程文件夹的路径 -->

         <property name="prefix" value="/WEB-INF/views/" />

         <!--后缀:扩展名 -->

         <property name="suffix" value=".jsp" />

</bean>

 

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

         <!-- 设置上传文件的最大尺寸为1MB -->

         <property name="maxUploadSize">

                  <!-- spring el写法:5MB -->

                  <value>#{1024*1024*5}</value>

         </property>

</bean>

8.1.4.   web.xml

1.post乱码过滤器

2.springmvc核心控制器

<!-- post提交中文乱码的过滤器 -->

<filter>

         <filter-name>CharacterEncodingFilter</filter-name>

         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

         <init-param>

                  <!-- 按照什么格式进行转码 -->

                  <param-name>encoding</param-name>

                  <param-value>UTF-8</param-value>

         </init-param>

</filter>

 

<filter-mapping>

         <filter-name>CharacterEncodingFilter</filter-name>

         <url-pattern>/*</url-pattern>

</filter-mapping>

 

<!-- 核心控制器 -->

<servlet>

         <servlet-name>dispatcher</servlet-name>

         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

         <init-param>

                  <param-name>contextConfigLocation</param-name>

                  <param-value>classpath:applicationContext.xml</param-value>

         </init-param>

         <load-on-startup>1</load-on-startup>

</servlet>

8.2.  集成MyBatis环境

8.2.1.   加入MyBatis相关9个 jar包(mybatis-spring-1.2.0.jar)

  1. 核心包
  2. 依赖包(删除一个commons-logging-1.1.1.jar)
  3. 数据库连接包

 

8.3.  Spring和MyBatis的集成方式

8.3.1.   框架集成核心

如果你的项目中,用到了Spring框架,那么其他框架主要就是和Spring集成!!

8.3.2.   和Spring集成的顺序

  1. 把当前框架的核心类,交给Spring管理
  2. 如果框架有事务,那么事务也要统一交给Spring管理

8.4.  Spring配置文件

8.4.1.   jdbc配置文件&数据源dataSource

<!-- Jdbc配置文件 -->

<context:property-placeholder location="classpath:jdbc.properties" />

 

<!-- 数据源dataSource -->

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

         <!-- 依赖注入连接池需要的属性 -->

         <!-- property name="是BasicDataSource的set方法,本质属性" -->

         <!-- property value="是jdbc.properties配置文件的key" -->

         <property name="driverClassName" value="${jdbc.driverClassName}" />

         <property name="url" value="${jdbc.url}" />

         <property name="username" value="${jdbc.username}" />

         <property name="password" value="${jdbc.password}" />

         <!--maxActive: 最大连接数量 -->

         <property name="maxActive" value="150" />

         <!--minIdle: 最小空闲连接 -->

         <property name="minIdle" value="5" />

         <!--maxIdle: 最大空闲连接 -->

         <property name="maxIdle" value="20" />

         <!--initialSize: 初始化连接 -->

         <property name="initialSize" value="30" />

         <!-- 连接被泄露时是否打印 -->

         <property name="logAbandoned" value="true" />

         <!--removeAbandoned: 是否自动回收超时连接 -->

         <property name="removeAbandoned" value="true" />

         <!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->

         <property name="removeAbandonedTimeout" value="10" />

         <!--maxWait: 超时等待时间以毫秒为单位 1000等于60秒 -->

         <property name="maxWait" value="1000" />

         <!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->

         <property name="timeBetweenEvictionRunsMillis" value="10000" />

         <!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->

         <property name="numTestsPerEvictionRun" value="10" />

         <!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->

         <property name="minEvictableIdleTimeMillis" value="10000" />

         <property name="validationQuery" value="SELECT NOW() FROM DUAL" />

</bean>

8.4.2.   SqlSessionFactoryBean

SSJ集成时候需要entityManagerFactory

SSM集成时候需要sqlSessionFactory

resources\MyBatis集成Spring文档资料\mybatis-spring整合文档.exe

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

8.4.3.   ssm注入顺序

jdbc.properties->dataSource->sqlSessionFactory->mapper(dao)->service->controller(action)

8.4.4.   获取sqlSessionFactory方案1

<!-- Mybatis的核心类 -->

         <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

                  <property name="dataSource" ref="dataSource" />

                  <property name="configLocation" value="classpath:mybatis-config.xml"></property>

         </bean>

 

         <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">

                  <property name="sqlSessionFactory" ref="sqlSessionFactory" />

                  <property name="mapperInterface" value="cn.itsource.ssm.mapper.UserMapper" />

         </bean>

 

         <context:component-scan base-package="cn.itsource.ssm"/>

8.4.5.   获取sqlSessionFactory方案2

<!-- 配置sqlSessionFactory -->

         <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

                  <!-- 注入数据源 -->

                  <property name="dataSource" ref="dataSource" />

                  <!-- 配置mybatis (mapper)映射器路径 -->

                  <property name="mapperLocations" value="classpath:cn/itsource/ssm/mapper/*Mapper.xml" />

                  <!-- 配置mybatis 类型别名 -->

                  <property name="typeAliasesPackage">

                           <value>

                                    cn.itsource.ssm.domain

                                    Cn.itsource.ssm.query  可能有查询对象

                           </value>

                  </property>

         </bean>

一劳永逸

         <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

                  <property name="basePackage" value="cn.itsource.ssm.mapper"></property>

         </bean>

 

 

 

  1. 课程总结

9.1.  重点

  1. SSM集成,操作必须非常熟练
  2. 多对一嵌套结果,嵌套查询
  3. 一对多嵌套结果
  4. 映射器接口

9.2.  难点

  1. 多对一嵌套结果:查询的有dept表的别名配置
  2. 映射文件里面使用${}的适用场景:order by
  3. 常见异常
  4. Caused by: java.io.NotSerializableException: cn.itsource.mybatis.day2._5_cache.User

要使用二级缓存,要求domain必须实现序列化接口

  1. 课后练习
  2. 面试题
  3. 扩展知识或课外阅读推荐

13.1.         扩展知识

  1. java.io.Serializable浅析

http://www.cnblogs.com/gw811/archive/2012/10/10/2718331.html

13.2.         课外阅读

posted @ 2019-07-20 19:04  龙龙鄂构  阅读(166)  评论(0编辑  收藏  举报