mybatis

Mybatis的简介和下载安装

什么是 MyBatis ?

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL 、存储过程以及高级映射。 MyBatis 避免了几乎所有的JDBC 代码和手动设置参数以及获取结果集。 MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的POJOs(Plain Old Java Objects, 普通的 Java 对象 ) 映射成数据库中的记录。

下载与应用准备

下载地址: https://github.com/mybatis/mybatis-3

文档地址:http://www.mybatis.org/mybatis-3/zh/index.html

安装与学习准备
1、先建立一个maven的java工程
2、要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于 classpath 中即可。
这里我们使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:

<dependencies>
   <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
     <version>3.4.6</version>
   </dependency>
   <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.46</version>   <!--8.0.18-->
    </dependency>
    <dependency><!--日志包,开发过程中其它可能依赖它-->
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.12</version>
     </dependency>
     <dependency><!--日志包,开发过程中其它可能依赖它-->
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency><!--日志包,开发过程中其它可能依赖它-->
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
        </dependency>
</dependencies>

log4j.properties:

log4j.rootLogger = debug,stdout, D
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %m%n
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ./log4j.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%d %p %m%n

3、建立一个数据库mybatisdemo,将sshweb数据库的表复制过去

image.png

MyBatis入门例子

1.建立与数据库对应的POJO类,有参构造函数一个(id不作参),无参构造函数一个,getxxx,setxxx一系列方法,toString()方法。该类中的字段名与数据表中的字段名要一样。不能改大小写。

image.png

2.建立mybatis的配置文件,在文档的入门中找到复制demo,修改后如下:

不像Hibernate有建文件工具,mybatis没有,只能以建文件的方式,建xml文件。再从官网文档复制配置文件

image.png

配置文件经修改加入到项目中的代码为:mybatisConfig.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
       <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
             <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
             <property name="url" value="jdbc:mysql://xiongshaowen.com:3306/mybatisdemo"/>
             <property name="username" value="root"/>
             <property name="password" value="xiong"/>
          </dataSource>
       </environment>
    </environments>
  <mappers>
     <mapper resource="cn/ybzy/mybatisdemo.model/UserMapper.xml"/>
  </mappers>
</configuration>

3.建立pojo到表中的映射文件UserMapper.xml


image.png

4.测试:Run as --junitTest

public class MybatisTest {
    @Test
    public void test1() throws IOException {
        //1.获取到创建数据库链接的会话的工厂类
        InputStream inputStream =                    Resources.getResourceAsStream("mybatisConfig.xml");
        SqlSessionFactory sqlSessionFactory =(SqlSessionFactory) new             SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂类,获取数据库连接的会话。
        SqlSession session = sqlSessionFactory.openSession();
        //通过会话操作数据库。
        try {
        User user =session.selectOne("cn.ybzy.mybatisdemo.model.selectOneUser",1);
        System.out.println(user);
        }finally {
            session.close();
        }
    }
}

上述各文件在项目中的位置,如图:

image.png

入门例子的升级

接口编程,即是传统hibernate的Dao层。

上一次课中查询一条User记录并封装到User对象中返回的功能是实现了,但是前面的实现的方法却是过时的,现在,我们更多的是实现面向接口来编程,所以Mybatis的查询方法就升级了:

1...建立与数据库对应的POJO类

2...建立mybatis的配置文件

3...在dao(mapper层)层你给model里的POJO类创建一个接口,不像Hibernate一样需要写它的实现类,这个接口是不直接写实现类的

1635114532555.png

4...修改上次课的POJO对象和Mysql数据的表之间的映射配置UserMapper.xml:

①namespace不能自定义了,必须写成上面的接口的全类名,UserMapper和接口实
现绑定
②select标签的id也不能自定义了,必须写成接口里的对应的方法的方法名,这里
是:getOneUserById,这样子sql语句和接口的方法绑定了


1635129794759.png
  1. 测试方法

测试类:src/text/java/cn.ybzy.mybatisdemo.mybatistest.java

public class mybatistest {
    @Test
    public void test1() throws IOException {
        //1.获取到创建数据库链接的会话的工厂类
        InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
        SqlSessionFactory sqlSessionFactory =(SqlSessionFactory) new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂类,获取数据库连接的会话。
        SqlSession session = sqlSessionFactory.openSession();
        //通过会话操作数据库。
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            //System.out.println(mapper);  //打印代理对象
            User user =mapper.getOneUserById(1);
            System.out.println(user);
        
        }finally {
            session.close();
        }
    }
    
}

解决mybatis的配置文件没代码提示的问题

1.将org.apache.ibatis.builder.xml包里的两个dtd文件复制出来,jar包里复制

如果是maven工程的话,本地仓库中找到org/mybatis/mybatis/3.4.xx

然后找到解压的文件夹xml,复制箭头所指的两个文件,到一个熟悉的目录下。
再如下图所示。复制两个配置文件uri,如下两图所示。


image.png

两个URI书写代码:

http://mybatis.org/dtd/mybatis-3-mapper.dtd
http://mybatis.org/dtd/mybatis-3-config.dtd

最后如下图所示,把两个文件加进工具中。这样重新打开两个个配文件后,就有代码提示了。

image.png
  1. 如果连线的话,想要得到两个文件,很简单,

mybatis-3-config.dtd,mybatis-3-mapper.dtd

全局配置文件与之中的标签

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息。文档的顶层结构如下:

configuration 配置
properties 属性
settings 设置
typeAliases 类型别名
typeHandlers 类型处理器
plugins 插件
environments 环境
environment 环境变量
transactionManager 事务管理器
dataSource 数据源
databaseIdProvider 数据库厂商标识
mappers 映射器

properties

--这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。主要作用是读取外面的properties属性文件的!这点外面应该不陌生,前面用过很多次了,将数据库的信息都单独配置在jdbc.properties文件里,这里做一遍看看就行了:

jdbc.properties

jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/mybatisdemo

注:与spring整合时,我们不用这个properties属性文件。该功能由spring来完成。

settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。

设置参数         描述           有效值    默认值
1.cacheEnabled
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。
true| false   
true   

2.lazyLoadingEnabled
延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆
盖该项的开关状态。
true| false
false

3.aggressiveLazyLoading
当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考 lazyLoadTriggerMe
thods).
true| false
false (truein ≤3.4.1)

4.multipleResultSetsEnabled 
是否允许单一语句返回多结果集(需要兼容驱动)。
true | false
true

5.useColumnLabel
使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
true|false
true
6.useGeneratedKeys
允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如Derby )。
true|false
False

7.autoMappingBehavior
指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射; PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
NONE,PARTIAL,FULL
PARTIAL
8.autoMappingUnknownColumnBehavior
指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)FAILING: 映射失败 ( 抛出 SqlSessionException)
NONE,WARNING,FAILING
NON

9.defaultExecutorType
配置默认的执行器。 SIMPLE 就是普通的执行器; REUSE 执行器会重用预处理语句( prepared statements ); BATCH 执行器将重用语句并执行批量更新。
SIMPLE,REUSEBATCH
SIMPLE

10.defaultStatementTimeout
设置超时时间,它决定驱动等待数据库响应的秒数。
任意正整数
Not Set (null)
11.defaultFetchSize
为驱动的结果集获取数量( fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。
任意正整数
Not Set (null)

11.safeRowBoundsEnabled
允许在嵌套语句中使用分页( RowBounds )。如果允许使用则设置为 false 。
true|false
False

12.safeResultHandlerEnabled
允许在嵌套语句中使用分页( ResultHandler )。如果允许使用则设置为 false 。
true | false
True

13.mapUnderscoreToCamelCase
是否开启自动驼峰命名规则( camel case )映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn的类似映射。
true | false
False

14.localCacheScope
MyBatis 利用本地缓存机制( Local Cache )防止循环引用( circular references )和加速重复嵌套查询。 默认值为SESSION ,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT ,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
SESSION |STATEMENT
SESSION

15.jdbcTypeForNull
当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL 、VARCHAR 或 OTHER 。
JdbcType常量 .大多都为 :NULL, VARCHAR and OTHER
OTHER

16.lazyLoadTriggerMethods
指定哪个对象的方法触发一次延迟加载。
用逗号分隔的方法列表。
equals,clone,hashCode,toString

17.defaultScriptingLanguage 
指定动态 SQL 生成的默认语言。
一个类型别名或完全限定类名。
org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

18.defaultEnumTypeHandler
指定 Enum 使用的默认 TypeHandler。 ( 从 3.4.5 开始 )
一个类型别名或完全限定类名。
org.apache.ibatis.type.EnumTypeHandler

19.callSettersOnNulls
指定当结果集中值为 null 的时候是否调用映射对象的 setter ( map 对象时为 put )方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型( int 、 boolean 等)是不能设置成 null 的。
true | false
false

20.returnInstanceForEmptyRow
当返回行的所有列都是空时, MyBatis默认返回 null 。 当开启这个设置时, MyBatis 会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collecti它也适用于嵌套的结果集 (i.e. collectioin and association) 。(从 3.4.2 开始)
true | false
false

21.logPrefix
指定 MyBatis 增加到日志名称的前缀。
任何字符串
Not set

22.logImpl
指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
SLF4J |LOG4J| LOG4J2 | JDK_LOGGING |COMMONS_LOGGING |STDOUT_LOGGING |NO_LOGGING
Not set

23.proxyFactory
指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。
CGLIB |JAVASSIST
JAVASSIST (MyBatis 3.3or above)

24.vfsImpl 
指定 VFS 的实现
自定义 VFS的实现的类全限定名,以逗号分隔。
Not set
25.useActualParamName
允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用 Java 8 编译,并且加上 -parameters 选项。(从 3.4.1 开始)
true| false
true

26.configurationFactory
指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的懒加载属性值。 这个类必须包含一个签名方法 static Configuration getConfiguration(). (从 3.2.3 版本开始 )
类型别名或者全类名 .
Not set

一个配置完整的 settings 元素的示例如下:

<configuration>
  <properties resource="jdbc.properties"></properties>
 <settings>
 <setting name="mapUnderscoreToCamelCase" value="true"/>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
 <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
 </settings>
</configuration>

举例:User类中字段注册日期:regDate,而对应的表中sb_users(驼峰名)字段为reg_date如果没用用到<setting name="mapUnderscoreToCamelCase" value="true"/>则会报错。

<mapper namespace="cn.ybzy.mybatisdemo.mapper.UserMapper">
  <select id="getOneUserById" resultType="cn.ybzy.mybatisdemo.model.User">
    <!-- select id,username,password,state,reg_date from sb_users where id=#{id}  -->
    select * from sb_users where id=#{id}
  </select>
 </mapper>

typeAliases和typeHandlers标签

typeAliases类型别名

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在用来减少类完全限定名的冗余

注: : :本人不配别名的,不利于检查。

这样单个的起别名很麻烦的,所有还有个搞法,批量起别名:

typeHandlers:自定义处理器

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型 。

这里边涉及到日期时间格式的字段有个小知识点: 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间API) 。JSR 310为Java提供了一个新的、改进的日期和时间API,以代替目前复杂的难以使用的日期和时间功能。 JSR310已经被添加到了JDK1.8中。主要提供了对java.util.Date的替代,另外还提供了新的DateTimeFormatter用于对格式化/解析的支持,使用起来方便很多:比如以前获取年、月、日很麻烦:

Year year = Year.now(); 年
YearMonth yearMonth = YearMonth.now(); 年-月
MonthDay monthDay = MonthDay.now(); 月-日
总之:JSR310提供了更好、更强大的、更易使用的日期时间API,具体的内容我们在jdk8新特性里讲

environments

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要
这么做。例如,开发、测试和生产环境需要有不同的配置。
不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能和其中一个绑定。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推。
环境元素定义了如何配置环境。

image.png

例:全局配置文件多数据库支持
这里我们来做一个对比试验,将上门课中使用hibernate做的权限控制项目,从mysql数据库转移到orcale数据库,然后我们在让现在的mybatis入门demo也从使用mysql数据库改orcale,对比一下两者之间的区别:

参考:https://www.jianshu.com/p/d5dee2931362这里我只讲mybatisdemo项目的,主要讲述连接oracle与mysql两种数据库系统的方法。

1--导oracle驱动包。

<dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.4</version>
        </dependency>

2--mybatis核心配置文件配两个环境变量,再配厂商标志<databaseIdProvider type="DB_VENDOR">。决定用到哪种数据库是由default决定的 <environments default="oracle">

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <properties resource="jdbc.properties"></properties>
   <settings> 
      <!--是否开启自动驼峰命名规则( camel c
        ase )映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn的类似映射。
        即模型类的字段如regDate与表中的字段reg_date自动识别,不用修改字段一样的  -->
      <setting name="mapUnderscoreToCamelCase" value="true"/>
   </settings>
    <environments default="oracle">
       <environment id="mysql">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
             <property name="driver" value="${jdbc.driverClass}"/>
             <property name="url" value="${jdbc.jdbcUrl}"/>
             <property name="username" value="${jdbc.user}"/>
             <property name="password" value="${jdbc.password}"/>
          </dataSource>
       </environment>
       
       <environment id="oracle">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
             <property name="driver" value="${orcl.driverClass}"/>
             <property name="url" value="${orcl.jdbcUrl}"/>
             <property name="username" value="${orcl.user}"/>
             <property name="password" value="${orcl.password}"/>
          </dataSource>
       </environment>
    </environments>
    
    <!-- ORACLE数据库要配的厂商标志,value给标志起别名-->
    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <!-- <property name="SQL Server" value="sqlserver"/> -->
     </databaseIdProvider>
  <mappers>
     <mapper resource="cn/ybzy/mybatisdemo/model/UserMapper.xml"/>
  </mappers>
  
</configuration>

3--连接oracle的配置文件jdbc.properties

orcl.user=root   
orcl.password=xiong
orcl.driverClass=oracle.jdbc.OracleDriver
orcl.jdbcUrl=jdbc:oracle:thin:@xiongshaowen.club:1522:sshwebeSID

4--User映躰表的映射文件 UserMapper.xml,其它,UserMapper.java接口类,User.java模型类不变。
注:databaseId="xxx",用来作核心配置文件识别是oracle还是mysql来操作的sql语句方法,如果在项目大的情况下,这种sql语句是天量的,这显然比hibernate的全自动化麻烦多了。hibernate语句的hql语句不管是对那个库是一样的。

<?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="cn.ybzy.mybatisdemo.mapper.UserMapper">
  <select id="getOneUserById" resultType="cn.ybzy.mybatisdemo.model.User" databaseId="mysql">
    <!-- select id,username,password,state,reg_date from sb_users where id=#{id}  -->
    select * from sb_users where id=#{id}
  </select>
 
   <select id="getOneUserById" resultType="cn.ybzy.mybatisdemo.model.User" databaseId="oracle">
    <!-- select id,username,password,state,reg_date from sb_users where id=#{id}  -->
    select * from t_students where id=#{id}
  </select>
</mapper>

5.在orcale中创建表:

create table sb_users (
id int ,
username varchar(50) ,
password varchar(100) ,
state int ,
reg_date date
);
insert into sb_users (id, username, password, state, reg_date)values('2','admin2','172eee54aa664e9dd0536b063796e54e','1',to_date('2014-02-14','yyyy-mm-dd'));
insert into sb_users (id, username, password, state, reg_date)values('3','mmm','5c79b8413fe81742a4147c9373771394','1',to_date('2018-08-05','yyyy-mm-dd'));
insert into sb_users (id, username, password, state, reg_date)values('4','ttt','882eebb5c59d44a3b85c4adbe54d1ee','1',to_date('2018-08-05','yyyy-mm-dd'));

6.测试,用到'入门例的升级'中的测试类代码。

文件映射器(mappers)

---既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL ),或类名和包名等。


<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

这样方式,前面已讲


<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>


<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

class方式应用,注意:

1.--现在的例子中直接这样用,是绝对不行的,因为我的映射文件和接口没放在同一个包了,沿用了hibernate时的习惯,把映射文件放到了model包里了!

2.--所以想要使用class属性来讲mapper文件注入到全部配置文件里,就必须保证mapper的文件名和接口的文件名一样,只是后缀不一样,且必须都在一个包目录里才ok。

3.--使用class属性来配置,还可以使用注解的方式来关联sql语句,这样子就可以不要UserDao.xml


image.png

-----在项目开发中,我们有这样的一个建议,注解上的sql往往是比较简单的和不怎么可能写好后会修改的,放这里,因为打包发布后,java文件是不能修改的,比较复杂和重要的,可能会发布以后要修改的sql语句,我们放到xml配置文件里,这样便于以后维护。


<mappers>
<package name="org.mybatis.builder"/>
</mappers>

这样的方式是上面一种的批量简化写法,所以,要求必须和上面的一样, xml 文件和接口必须同名,且在同一个包里才得行!当然这里提一句,有的工程师,不喜欢接口和 xml 文件在一个包里的视觉效果,但我好像无所谓,但是在视觉上也可以实现不在一个包里,怎么操作呢?新建一个资源文件夹,并在这个资源文件夹下建立一个和接口类所在的包名,一样的包,把 xml文件放这里,就可以搞定!视觉上号上不在一起,但发布后都会在 classes 这个类路径下了!

--以后在工程中呢,我就用最后的这种方式来做了,所以我的xml文件和接口的名字必须一样,且都在同一个包里!


image.png

映射文件Mapper XML 文件

---MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。 MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。

select标签resultType,resultMap属性的使用

  1. resultType属性
    11.返回的结果是List集合的类型,select标签里的resultType类型设置为List集合里的元素(User)类型,不用设置为List,与获取单个记录的方法类型一样。.
    下面分别是:接口类中定义的方法,映射文件中的方法(图片中)),测试类的中代码
public List<User> getAllUsers();          //查询所有用户,方法在映射文件中直接查全表。select* from 表
image.png
//测试,其它代码参上面的mybatistest.java            
               List<User> allUsers = mapper.getAllUsers();
              for(User user:allUsers) {
                  System.out.println(user);
                  System.out.println(user.getUsername());
              }

12.返回一个Map集合,key是列名称,value是列对应的值。map是mybatis定义一个Map类型的别名,是固定写法。

public Map<String, Object> getMapUsersById(Integer id);
image.png
try{ 
    UserMapper mapper = session.getMapper(UserMapper.class);
       Map<Integer,Object> map = mapper.getMapUserById(1);
       System.out.println(map);
  }finally{
     session.close();
  }     
//控制台打印如 {}里的东西。如果时List打印的是数组 [[]]
//{reg_date=2021-10-21 04:56:03.0, password=172eee54aa664e9dd0536b063796e54e, id=1, state=1, username=admin}

----返回的查询结果也是Map集合,但我们要这样子的结构:Map<Integer,User>,Map中的key是User的id,Map的值是User对象,实现它的关键是我们要在接口的方法上加一个注解,告诉Mybatis用哪个字段做Map的key:

//接口中方法定义 查询所有记录,封装到Map集合中。由注解定义的key找到对应的User记录,该例中注解指定id为key,当然也可以用其它字段作为key,如username 
   @MapKey("id")  //告诉Mybatis用哪个字段做Map的key:
   public Map<Integer ,User> getMapAllUsers();           //获取所有用户记录,放进map集合,map的key为id,Object为User
//映舑方法中的查询方法,返回Map
   <select id="getMapAllUsers" resultType="map">
      select * from sb_users
    </select>
//测试类:
   Map<Integer,User> map = mapper.getMapAllUsers();
   System.out.println(map);
   System.out.println(map.get(1));
//控制台输出结果:
//{1={reg_date=2021-10-21 04:56:03.0, password=172eee54aa664e9dd0536b063796e54e, id=1, state=1, username=admin}, 2={reg_date=2021-10-10 00:00:00.0, password=bf37bdf0c1971a33db7fa2c365192cd7, id=2, state=1, username=xiongshaowen}, 5={reg_date=2021-10-29 11:04:23.0, password=123456, id=5, state=1, username=xlz}, 6={reg_date=2021-10-29 12:23:57.0, password=123456, id=6, state=1, username=xlz}, 7={reg_date=2021-10-29 14:22:49.0, password=123456, id=7, state=1, username=熊凌洲}}
//{reg_date=2021-10-21 04:56:03.0, password=172eee54aa664e9dd0536b063796e54e, id=1, state=1, username=admin}
  1. resultMap的使用
    21.驼峰命名处理。javabean--regDate,表中--reg_date
    ---前面我们介绍了resultType属性的使用,其实Mybatis中的select标签,还有其他很多属性,前面我们已经讲了几个,接着还会讲几个相对重要的,其他的,同学们可以参照一下手册( http://www.mybatis.org/mybatis-3/zh/sqlmap-t xml.html#select )自己学习一下,下一个讲的属性是resultMap
    image.png

    ---手册里resultType和resultMap介绍有点模糊,说清楚了resultType是期望的返回的类型,集合的话是集合包含的类型,不是集合本身,且这两个不能同时使用,resultMap没说清楚,这里我们明确一下:其实两者的关系就是resultType解决数据表上的字段名称和JavaBean上的属性名称不一致的时候,前面介绍了两种方案,①在sql语句上起别名;②设置全局变量
    ----mapUnderscoreToCamelCase让符合驼峰命名约定的两个名字建立关联,如JavaBean属性名称regDate对应数据库字段名reg_date;其实这里还有第三种解决方案就是resultMap,它可以让我们指定定义数据库里的字段名称对应JavaBean的属性名称。
    select属性resultMap的使用
//1.接口UserMapper.java定义方法
public User getOneUserById(Integer id);
//2.映射文件UserMapper.xml中定义查询方法,返回的类型,与外部引用的定义
<!--下面的外部引用中定义了,模型类中的字段与表中字段的对应关系,这样就可以不用两者名字致了,如:regDate  reg_date  -->
    <select id="getOneUserById" resultMap="user_rsm">
      select * from sb_users where id=#{id}
    </select> 
    <resultMap type="cn.ybzy.mybatisdemo.model.User" id="user_rsm">
       <id property="id" column="id"/>
       <result property="password" column="password"/>
       <result property="regDate"  column="reg_date"/>
       <result property="username" column="username"/>
       <result property="state"    column="state"/>
    </resultMap>
//3.测试代码
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            //System.out.println(mapper);  //代理对象,该对象是映射到整个表中了。
        //查询一个记录,以resultMap定义的结果集返回,该定义了别名,与引用别名对象,灵活处理了类字段名与表字段名致的情况如:regDate,reg_date的名字不一致场景
            User user = mapper.getOneUserById(1);
            System.out.println(user);
}finally{
   session.close();
}

22.使用resultMap实现关联查询 (在hibernate中是自动完成的,Mybatis得我们手工配置)
试验步骤:
①新增Role类,在User你新增属性Role role;

public class Role {
    private int id;
    private String roleName;
    private List<User> users; //一个角色关联很多个用户
    -----get,set,toString()等方法----------
}
--------------------------------------------------------------------------------------------------------------------
public class User {
    private int id;
    private String username;
    private String password;
    private int state;
    private Date regDate;
    private Role role;   //一个用户对应一个角色,不可有多个角色
   -----get,set,toString()等方法----------
}

这里不像hibernate中那样,会自动根据模型类的映射文件自动创建关系表和类表,这里我们要手动创建表。
sb_roles, --id自增,主键,非空(照片拍早了)

image.png

加几条记录
image.png

sb_users表中添加一个字段role_id ,int类型。再把各用户的role_id加一下,其一对多(一个角色:多个用户)的关系在程序中实现。
image.png

在mysql库连接工具中设计关联查询sql语句,看是否查询成功
image.png

②在UserDao.xml重写sql和resultMap(复杂的查询结果resultType是吃不够的,必须用resultMap)
image.png

③编写对应的resultMap( 通过级联的方式建立关联 ):

<select id="getOneUserById" resultMap="user_rsm">
        select u.id,u.username,u.password,u.reg_date,u.state,r.id rid,r.role_name 
         from sb_users u,sb_roles r where u.role_id=r.id and u.id=#{id}
     </select> 
    <resultMap type="cn.ybzy.mybatisdemo.model.User" id="user_rsm">
       <id property="id" column="id"/>
       <result property="password" column="password"/>
       <result property="regDate"  column="reg_date"/>
       <result property="username" column="username"/>
       <result property="state"    column="state"/>
       <result property="role.id" column="rid"/>
       <result property="role.roleName" column="role_name"/>
    </resultMap>

④看测试,实现关联查询

 try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            //System.out.println(mapper);  //代理对象,该对象是映射到整个表中了。
        //查询一个记录,以resultMap定义的结果集返回,该定义了别名,与引用别名对象,灵活处理了类字段名与表字段名致的情况如:regDate,reg_date的名字不一致场景
            User user = mapper.getOneUserById(1);
            System.out.println(user);
}finally{
   session.close();
}
//User [id=1, username=admin, password=172eee54aa664e9dd0536b063796e54e, state=1, regDate=Thu Oct 21 04:56:03 CST 2021, role=Role [id=1, roleName=超级管理员]]

工具MyBatis Generator(MBG)

-----------MyBatis Generator(MBG),这是官方帮我们提供的一个自动生成代码的工具,前面的课程中,我们都是脑袋里想好,pojo有哪些属性,属性的类型是什么,对应的数据表中的字段名字是什么,匹配的类型是什么.....然后还要写接口xxxDao,以及它的实现配置文件xxxDao.xml等等都是手动自己操作,以前我们学习Hibernate的时候,感觉方便就是写好pojo启动服务器Hibernate会自动帮助我们生成对应的数据表,MyBatis也有类似的工具,MBG就是官方给我提供的这样的工具,但它和Hibernate有点不一样就是,Hibernate帮我们生成表,MBG帮我们根据表生成接口、pojo类和xml这些文件!方向是反的。
工具地址:https://github.com/mybatis下载jar包
Maven工程中直接pom.xml中导入jar包

<dependency>
   <groupId>org.mybatis.generator</groupId>
   <artifactId>mybatis-generator-core</artifactId>
   <version>1.3.7</version>
</dependency> 

以下元素就是MBG的最小配置(mbgConfig.xml在main--java--sources下)
<jdbcConnection> 元素指定如何连接数据库
<javaModelGenerator> 元素指定生成Model的目标package与目标project
<sqlMapGenerator> 元素指定生成Mapping XML文件的目标package与目标project
(Optionally) <javaClientGenerator> 元素指定生成Mapper(即DAO)文件的目标package与目标project, 如果不指定
这个元素就不会生成Mapper文件
至少一个 <table> 元素

下面是一个较为完整的示例, 可以保存下来按需修改

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- 数据库的驱动, JAR/ZIP文件的全路径,maven工程,驱动已经依赖了,没用 -->
    <classPathEntry
        location="/Program Files/IBM/SQLLIB/java/db2java.zip" />
    <!--targetRuntime用MyBatis3, 也就是默认的, 其他我基本不会用-> <context id="DB2Tables" targetRuntime="MyBatis3"> 
        <commentGenerator> <!-- 去除自动生成的注释 -->
    <property name="suppressAllComments" value="true" />
</commentGenerator>
    <!--基础的数据库连接 -->
    <jdbcConnection
        driverClass="COM.ibm.db2.jdbc.app.DB2Driver"
        connectionURL="jdbc:db2:TEST" userId="db2admin" password="db2admin">
    </jdbcConnection>
    <!--Java类型解析器, 目前也就只有forceBigDecimals可以给你玩 -->
    <javaTypeResolver>
        <!--当数据类型为DECIMAL或者NUMERIC的时候, 如果是true的话则总是使用java.math.BigDecimal -->
        <!--以下是false, 即默认值的情况 -->
        <!--如果有小数或者decimal长度大于18, Java类型为BigDecimal -->
        <!--如果没有小数, 以及decimal长度为10至18, Java类型为Long -->
        <!--如果没有小数, 以及decimal长度为5至9, Java类型为Integer -->
        <!--如果没有小数, 以及decimal长度少于5, Java类型为Short -->
        <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>
    <!--Domain生成器 -->
    <javaModelGenerator targetPackage="test.model"
        targetProject=".\src\main\java">
        <!--据说可以自动添加schema名, 可是我没用到过 -->
        <property name="enableSubPackages" value="true" />
        <!--生成全属性构造器, 没什么用, 如果有指定immutable元素的话这个会被忽略 -->
        <property name="constructorBased" value="true" />
        <!--生成不可变的domain, 这个我也很少用 -->
        <property name="immutable" value="true" />
        <!--每个Domain都继承这个bean -->
        <property name="rootClass"
            value="com.github.prontera.domain.base.BasicEntity" />
        <!--当遇到String的时候setter是否会先trim() -->
        <property name="trimStrings" value="true" />
    </javaModelGenerator>
    <!--Mapping生成器 -->
    <sqlMapGenerator targetPackage="test.xml"
        targetProject=".\src\main\java">
        <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
    <!--Mapper生成器, 当type为ANNOTATEDMAPPER时是带有@annotation的Mapper, MIXEDMAPPER是XML文件 -->
    <javaClientGenerator type="XMLMAPPER"
        targetPackage="test.dao" targetProject=".\src\main\java">
        <property name="enableSubPackages" value="true" />
        <!--每个Mapper所继承的接口 -->
        <property name="rootInterface"
            value="com.github.prontera.Mapper" />
    </javaClientGenerator>
    <!--字段命名策略过程: <columnRenamingRule> >> property name="useActualColumnNames" -->
    <!--alias属性是个神器, 会为所有SQL都添加, 做关联的时候就非常方便了 -->
    <!--至于什么Example, 全关了就是 -->
    <table alias="ha" tableName="ALLTYPES"
        domainObjectName="Customer" enableCountByExample="false"
        enableUpdateByExample="false" enableDeleteByExample="false"
        enableSelectByExample="false" selectByExampleQueryId="false">
        <!--指定是否用数据库中真实的字段名, 而不是采用MBG转换后的驼峰 -->
        <property name="useActualColumnNames" value="true" />
        <!--自动集成改类 -->
        <property name="rootClass"
            value="com.github.prontera.domain.base.HelloBasicClass" />
        <!--Mapper自动继承的接口 -->
        <property name="rootInterface"
            value="com.github.prontera.Mapper" />
        <!--当遇到String的时候setter是否会先trim() -->
        <property name="trimStrings" value="true" />
        <!--先进行columnRenamingRule, 再进行useActualColumnNames. 如果有columnOverride则忽略该配置 -->
        <!--关于columnRenamingRule的具体例子 http://www.mybatis.org/generator/configreference/columnRenamingRule.html -->
        <columnRenamingRule searchString="^CUST_"
            replaceString="" />
        <!--顾名思义, 忽略某些列 -->
        <ignoreColumn column="CREATE_TIME" />
        <!--也是忽略数据列, 但是可以通过正则表达式, except子元素是可选的, 代表忽略除UPDATE_TIME外的列 -->
        <ignoreColumnsByRegex pattern=".*_TIME$">
            <except column="UPDATE_TIME" />
        </ignoreColumnsByRegex>
    </table>
</context>
</generatorConfiguration>

本人例子:根据现在的sb_users,sb_roles,sb_permissions三个表建立对应的模型类,映射文件,接口文件。

本人配置的mbgConfig.xml文件与测试代码如下:

<generatorConfiguration>

    <!--targetRuntime用MyBatis3, 也就是默认的, 其他我基本不会用 -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator> <!-- 去除自动生成的注释,如java类中的注释,很长长,烦 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!--基础的数据库连接 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
            connectionURL="jdbc:mysql://xiongshaowen.com:3306/mybatisdemo?allowMultiQueries=true"
            userId="root" password="xiong">
        </jdbcConnection>

        <!--Java类型解析器, 目前也就只有forceBigDecimals可以给你玩 -->
        <javaTypeResolver>

            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--Domain生成器--模型类 -->
        <javaModelGenerator
            targetPackage="cn.ybzy.mybatisdemo.model"
            targetProject=".\src\main\java">

        </javaModelGenerator>

        <!--Mapping生成器 -->
        <sqlMapGenerator
            targetPackage="cn.ybzy.mybatisdemo.mapper"
            targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!--Mapper生成器--类似DAO接口, 当type为ANNOTATEDMAPPER时是带有@annotation的Mapper, MIXEDMAPPER是XML文件 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.ybzy.mybatisdemo.mapper"
            targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />

        </javaClientGenerator>

        <!--字段命名策略过程: <columnRenamingRule> >> property name="useActualColumnNames" -->
        <!--alias属性是个神器, 会为所有SQL都添加, 做关联的时候就非常方便了 -->
        <!--至于什么Example, 全关了就是 -->
        <table tableName="sb_users" domainObjectName="User"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
        </table>
        <table tableName="sb_roles" domainObjectName="Role"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
        </table>
        <table tableName="sb_permissions" domainObjectName="Permission"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
        </table>
    </context>
</generatorConfiguration>

运行mgb工具,自动生成对应表的文件。

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
public class mybatistest{
@Test
    public void mgbtest() throws SQLException, IOException, InterruptedException, XMLParserException, InvalidConfigurationException {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        String path = this.getClass().getClassLoader().
                   getResource("mgbConfig.xml").getPath();
        File configFile = new File(path);
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
}

测试工具创建的文件:用到工具自动创建的UserMapper.xml文件中的查询selectByPrimaryKey(id)映射方法

public class mybatistest {
    @Test
    public void test1() throws IOException {
    
        //1.获取到创建数据库链接的会话的工厂类
        InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
        SqlSessionFactory sqlSessionFactory =(SqlSessionFactory) new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂类,获取数据库连接的会话。
        SqlSession session = sqlSessionFactory.openSession();
        //通过会话操作数据库。
        try {
            UserMapper userMapper = session.getMapper(UserMapper.class);
            User user = userMapper.selectByPrimaryKey(1);
            System.out.println(user);
        }finally {
            session.close();
        }
    }
 }

注:创建这些文件之前我们要先创建数据库,和表,不必建关联关系如外键舍的,这些在映射文件写代码处理。

创建重复映射方法处理:

----也不知道什么原因,自动创建的映射文件两有两遍一样的代码,但第一遍中少一个字段,而重复一遍的代码中的方法没有少字段,把前面的删除了。模型类少一个字段,自已补上了。

Mybatis的分页插件

插件文档地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md

  1. 导入jar包:
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.1.4</version>
</dependency>
  1. 在Mybatis中全局配置文件中配置拦截器
........................
</settings>
   <plugins>
      <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
   </plugins>
<environments default="mysql">
......................
  1. 以用户分页查询为例:UserMapper.java,UserMapper.xml
    一页5条记录,显示第一页
//查询所有用户信息,分页
    public List<User> selectAllUser();
---------------------------------------------------------------------------------------------------------------------------------------
    <!--自定义的查询方法,获取所有用户信息   分页  -->
  <select id="selectAllUser" resultType="cn.ybzy.mybatisdemo.model.User">
      select 
         <include refid="Base_Column_List"/>
         from sb_users
  </select>
-------------------------------------------------------------------------
      try{
      UserMapper userMapper = session.getMapper(UserMapper.class);
            Page<User> page = PageHelper.startPage(1,5);
            List<User> users = userMapper.selectAllUser();
            PageInfo<User> pageInfo = new PageInfo<>(users);
            System.out.println(users);
            for(User user:users){
                System.out.println(user);
            }
            System.out.println("第"+page.getPageNum()+"页");
            System.out.println("每页有"+page.getPageSize()+"条记录");
            System.out.println("表中总共有"+page.getTotal()+"条记录");
            System.out.println("显示第一页,一面在有5条记录,还有下一面吗?"+( pageInfo.isHasNextPage() ? "有": "没有"));
            }finally{
               session.close();
        }
        
1636408856520.png

Mybatis做批量操作

前面我们将动态标签foreach的时候,做过批量操作,但是foreach只能处理记录数不多的批量操作,数据量大了后,先不说效率,能不能成功操作都是问题,所以这里讲一讲Mybatis正确的批量操作方法:

  1. 在获取opensession对象的时候,我们可以传入参数,告诉Mybatis我要批量操作:


    1636409150939.png
  2. 测试批量插入10000条记录:


try{
            UserMapper userMapper = session.getMapper(UserMapper.class);
            long start = System.currentTimeMillis();
            for(int i=0;i<=1000;i++) {
                userMapper.insertSelective(new User("aaa"+i,"ppp"+i,1,new Date()));//要在User.java中创建该构造方法,再创一个无参构造方法
            }
            long end = System.currentTimeMillis();
            session.commit();
            System.out.println((end-start)/1000+"秒");
        
        }finally {
            session.close();
        }

调用存储过程

  1. 在mysql数据库中创建一个存储过程:


    1636423776495.png
DELIMITER $$
CREATE
 PROCEDURE `mybatisdemo1`.`getusersp2`(IN sid INT,IN eid INT)
BEGIN
  SELECT * FROM sb_users WHERE id>=sid AND id<=eid;
END$$
DELIMITER ;

在Mapper接口里创建方法,和普通的查询数据方法没区别:

  1. 在Mapper接口里创建方法,和普通的查询数据方法没区别:


    1636423790717.png
  2. 在Mybatis中调用存储过程


    1636423860248.png
<!-- 自定义查询方法,调用存储过程,resultMap与resultType="cn.ybzy.mybatisdemo.model.User"都可以 
   IN示参数模式为输入,call是调用命令,getusersp2是在数据库中创建的存储过程名字。
-->
      <select id="getUsersByProcedure" resultMap="BaseResultMap" statementType="CALLABLE">
          { call getusersp2(
             #{sid,mode=IN,jdbcType=INTEGER},
             #{eid,mode=IN,jdbcType=INTEGER}
          )}
      </select>

注:jdbcType=...不能乱填,可以这样去查询:Navigate---Open Type

1636424577987.png

1636424636193.png

               UserMapper userMapper = session.getMapper(UserMapper.class);
            List<User> users = userMapper.getUsersByProcedure(1,9);
            System.out.println(users);
//打印结果:
//[User [id=1, username=admin, password=172eee54aa664e9dd0536b063796e54e, state=1, regDate=Thu Oct 21 04:56:03 CST 2021, roleId=1], User [id=2, username=xiongshaowen, password=bf37bdf0c1971a33db7fa2c365192cd7, state=1, regDate=Sun Oct 10 00:00:00 CST 2021, roleId=1], User [id=5, username=xlz, password=123456, state=1, regDate=Fri Oct 29 11:04:23 CST 2021, roleId=2], User [id=6, username=xlz, password=xiong, state=1, regDate=Sat Nov 06 00:55:27 CST 2021, roleId=3]]

SSM整合(spring,springmvc,mybatis)

  1. 创建一个maven工程,导入jar包,pom.xml的配置如下:
<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-spec</artifactId>
            <version>1.2.5</version>
            <type>bundle</type>
        </dependency>
        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-impl</artifactId>
            <version>1.2.5</version>
            <type>bundle</type>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.4</version>
        </dependency> 
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    </build>
  1. 在web.xml里做spring和springmvc的初始配置
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
   <!-- 整个web应用范围的初始化参数,给spring框架指定配置文件的位置 -->
   <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </context-param>
    <!-- spring和javaweb应用之间的一个连合 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 设置springmvc的调度器-->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!--springmvc拦截所有的本web应用,再结合情识各个调度  -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. SpringMVC只扫描Controller和ControllerAdvice:
<!-- 配置包扫描 -->
<context:component-scan base-package="com.hy.springmvc" use-default-
filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
 <context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<context:component-scan base-package="cn.ybzy.ssmweb" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler/>

查询获取到的map对象转为实体类对象

public class MapToEntityTool {
    /**
     * 缓存类的属性信息
     */
    private static Map<String, EntityCacheItem> convertItemCache = new HashMap<>();

    /**
     * map to entry 的泛型方法 功能:把map对象转化为entity实体对象(entityClass对应的)
     * 原理:首先获取参数entityClass的所有字段,与方法,所有字段名放在fieldNameList集合中,所有方法放在Map集合中
     * 其次:获取mybatis传递过来的Map集合中的所有键值(键名与entityClass传递过来的字段名一样,不一样不做处理) 所有发生的异常,return
     * null;这样不留垃圾
     * 
     * @return
     */
    public static <T> T map2entity(Map<Object, Object> map, Class<T> entityClass) {
        EntityCacheItem entityCacheItem = convertItemCache.get(entityClass.getName());
        if(entityCacheItem ==null) {
            entityCacheItem = EntityCacheItem.createEntityCacheItem(entityClass);
            convertItemCache.put(entityClass.getName(),entityCacheItem);
        }
        //entityClass拿到参数传来的对象的属性名秒称(List)集合
        List<String> fieldNameList = entityCacheItem.getFieldNameList();
        //通过entityClass参数,获取类型里边的set方法的map集合
        Map<String,Method> setMethodMap=entityCacheItem.getSetMethodMap();
        System.out.println("数据库中查询的结果集:"+map);
        System.out.println("实体类对象中的属性名字:"+fieldNameList);
        Map<Object, Object> targetMap = new HashMap<>();
        String key;
        String key1;
        String key2;
        for(Map.Entry<Object, Object> entry:map.entrySet()) {
            key = entry.getKey().toString();
            while(key.contains("_")) {
                //add_date  a_b_c_d
                key1 = key.substring(0,key.indexOf("_")); //add
                key2 = key.substring(key.indexOf("_")+1); //date
                key = key1 + key2.substring(0,1).toUpperCase()+key2.substring(1); //addDate
            }
            targetMap.put(key, entry.getValue());
        }
        T entity = null;
        try {
            entity = entityClass.newInstance(); // 反射方式创建一个对象(实例entity),此时它还是空的,下面要注入内容
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } // 通过反射方式,获取这个类型的对象

        Object mapFieldValue = null;
        Method setMethod1 = null;
        Class<?>[] parameterTypes = null;
        for (String fieldName1 : fieldNameList) { // 循环拿到Map集合(调用时参数传过来的map)中的键:循环把map所有的键值注入到实体中(如:User会对应很多setXXX设置字段的方法)
            mapFieldValue = targetMap.get(fieldName1);
            if (mapFieldValue == null)
                continue; // 如果键不存在,就没有做本次循环的必要,继续进行一下次循环
            setMethod1 = setMethodMap.get(fieldName1); // 如果方法不存在,也没有。。。。
            if (setMethod1 == null)
                continue;
            parameterTypes = setMethod1.getParameterTypes(); // 获取方法中的所有类型的参数数组
            if (parameterTypes == null || parameterTypes.length > 1) { // 如果setxxx方法中参数没有或大于1个时也不做本次循环,因为setUsername(xx)中只有一个参数
                continue;
            }
            if (parameterTypes[0].isAssignableFrom(mapFieldValue.getClass())) {
                // 若map传来的属性值的类型和set方法中参数的类型一致
                // 如:setUsername(Object)的object,与mapFieldValue的类型是否一致
                try {
                    setMethod1.invoke(entity, mapFieldValue);// 调用是对象的set方法把属性值注入到实体里(entity--不是键值对的)
                                                                // 如:setUsername("xiongshaowen");
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            } else {
                //这里输出让我们看到,封装时封装的数据类型,基本数据类型为它的包装类,特别是模型类中int id;要定为Integer id;
                System.out.println(
                        "不同类型:set方法中的参数类型:" + parameterTypes[0] + "======数据库中查询的结果集中数据类型:" + mapFieldValue.getClass());
            }
        }

        return entity;
    }
    // 定义一个缓存(静态存储)内部类,实例化后有把传来的类封装字段和方法到List,Map集合中的功能。把map转换实例时,会非常频繁的造访下面代码,这样我们定义一个缓存减少系统消耗
        static class EntityCacheItem {
            private EntityCacheItem() {
            }; // 私有化构造(无参)方法,让该类不可在外部实例化,要通过内部方法来创建实例对象

            private List<String> fieldNameList = new ArrayList<String>();
            private Map<String, Method> setMethodMap = new HashMap<>();

            public List<String> getFieldNameList() {
                return fieldNameList;
            }

            public Map<String, Method> getSetMethodMap() {
                return setMethodMap;
            }

            public void parseEntity(Class<?> entityClass) {
                Field[] fields = entityClass.getDeclaredFields(); // 获取所有字段,不管私有还是公有
                String fieldName;
                String setMethodName;
                Method setMethod = null;
                setMethodMap = new HashMap<>();
                for (Field field : fields) {
                    field.setAccessible(true); // 获取可修改字段的权限
                    fieldName = field.getName(); // 属性字段的名字
                    fieldNameList.add(fieldName);
                    setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 设置类似javabean方法,如:setUsername(Username)
                    try {
                        setMethod = entityClass.getDeclaredMethod(setMethodName, field.getType()); // 拿到实体对应的类中的setUsername(Object
                                                                                                    // param)方法
                    } catch (Exception e) {
                        e.printStackTrace();

                    }
                    setMethodMap.put(fieldName, setMethod); // 如:Map集合中的一个元素{username,setUsername(Object param)}
                }
            }

            // 通过内部创建该类实例对象,
            public static EntityCacheItem createEntityCacheItem(Class<?> entityClass) {
                EntityCacheItem ci = new EntityCacheItem();
                ci.parseEntity(entityClass);
                return ci;
            }
        }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Mybatis-9.28 环境: JDK1.8 Mysql 5.7 maven 3.6.1 IDEA 回顾: JD...
    眼若繁星丶阅读 216评论 0 1
  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有...
    Mzr_e487阅读 347评论 0 1
  • Mybatis 1. JDBC操作分析 1、频繁创建、释放数据库连接,影响系统性能。 2、SQL硬编码、不易维护。...
    左师兄zuosx阅读 427评论 0 0
  • Mybatis-9.28 环境: JDK1.8 Mysql 5.7 maven 3.6.1 IDEA 回顾: JD...
    友人Ay阅读 339评论 0 1
  • 简介 MyBatis[http://www.mybatis.org/mybatis-3] 是一款优秀的持久层框架,...
    Whyn阅读 958评论 0 0