Mybatis 学习

基础知识学习

框架:开发中说的框架,通常是一种解决方案,其内部封装了一些细节,使开发者可以通过很少的时间实现功能,提高开发效率。

三层架构

​ 表现层:用于展示数据。

​ 业务层:用于处理业务需求,我们自己编码实现。

​ 持久层:用于与数据库交互。

由于Mybatis是关于持久层的,后面就专注于持久层的知识。

持久层技术解决方案

  • JDBC技术:是jdk官方提供的数据库交互规范。
  • Spring中的JdbcTemplate:是spring提供的数据库交互工具,其在JDBC上进行了简单封装
  • Apache的DButils:与JdbcTemplate相似,也是实现了简单封装。

说明:上述三者都并非框架。JDBC是一套规范,后两者是该规范的简单实现。他们都只是工具,因为其并没有形成一套完整的解决方案,我们在开发中仍需要做很多事。

myBatis概述

​ mybatis是一个优秀的基于java 的持久层框架,其内部封装了jdbc,使开发者只需要关注sql语句本身,而不用花费精力去处理加载驱动、创建连接、创建statement等繁杂过程。

​ mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql的动态参数进行映射(如:Object.query("select * from tableName where id=?",db.getId())),生成最终的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。

​ 采用思想:ORM(Object Relational Mapping,对象关联映射),其解决了实体对象到数据库表中的映射问题。对jdbc进行了封装,屏蔽了jdbc api底层访问的细节,使开发者无需与其打交道,就可以完成数据库持久化操作。


Mybatis环境搭建

小细节${}#{}的区别:效果都是占位,然后等待编译时进行变量替换。区别在于#{}替换后,会加上引号,表明该值是字符串;而${}替换后是不加字符串的,可以用于properties文件的引用、sql语句中如ORDER BY ${}等。

注意:当使用${}作为sql语句时,内部是固定占位符:${value}

大致开发步骤

  • 1.建立maven工程并导入依赖
  • 2.创建实体类和dao的接口
  • 3.创建Mybatis的主配置文件:sqlMapConfig.xml
  • 4.创建每个dao对应的映射配置文件:SubjectsDAOInterface.xml

sqlMapConfig.xml




    
    
        
        
            
            
                
            
            
            
                
                
                
                
                
            
        
    
    
    
    
        
    

SubjectDAOInterface.xml




    
    

注意事项

  • mybatis中,DAO的含义与Mapper相同。所以SubjectsDAO和SubjectMapper其意思相同,都代表实体类与数据库的交互。
  • mybatis的映射配置文件,如上面代码SubjectDAOInterface.xml必须与dao接口的包结构相同。(mybatis的映射配置文件在resources文件夹下,但其包结构也跟SubjectDAOInterface一样。)
  • 映射配置文件SubjectDAOInterface.xml中,标签的属性namespace的取值必须为对应dao接口的全限定名。
  • 映射配置文件中,中的子标签如: select * from subjects; select last_insert_id(); INSERT into subjects(name,grade,enjoy) values(#{name},#{grade},#{enjoy}); DELETE from subjects where id = #{id}; UPDATE subjects set name = #{name},grade=#{grade},enjoy=#{enjoy} where id=#{id};

    标签

    主配置文件中

    注意:有顺序要求,否则会报错。如下依次。

    (properties, settings, typeAliases, typeHandlers, objectFactory, objectWrapperFactor, reflectorFactory, plugins, environments, databaseIdProvider, mappers)

    • :主标签,必须要命名空间才能用:。后面的标签都属于本标签的子标签。

    • :用于包含多个配置环境。属性:default,设置一个默认的标签。

    • :用于单个环境配置。属性:id,来标识该环境配置标签。

    • :事务管理标签。属性:type,指定事务管理器类型,一般是jdbc

      • 子标签::用来注入成员变量dataSource。
    • :用来配置数据源。属性:POOLED,表示连接池。

      • 子标签:多个:用来配置连接信息:驱动、url、用户名、密码。
    • :用来指定外部properties文件。属性:resource,表示文件位置。

    • :用于配置包下所有类的别名。在mybatis中,由于预先设置了别名,如resultType="int"可以写成resulttype="INt"并不区分大小写

      当我们自己想这样使用的,可以用该标签。

      • 子标签::用于给某个指定类添加别名。
      • 子标签::(*常用)用于给整个包下的所有类添加别名。属性:name="..."
        • 用处
        • ①用于给dao所在包在主配置文件添加别名,然后在映射配置文件中,就可以使用别名进行引用,而不用全限定名。
        • ②可以在标签中,直接写,可以替代;
    dao接口映射配置文件中:
    • :主标签。属性:namespace="path.to.daoInterface"

    • ..here..标签内部的语句,如:#{name}

      对象参数:(通常用作对实体类对象的再包装,开发中,由多个对象组成一个查询条件类,来实现数据查 询)

      ​ 当类对象作为方法参数时,若要用对象的成员变量的属性,如Class Condition{ User user;},传入的 参数是Condition 对象,但要使用其中的user成员变量的属性(成员变量)user.name。此时写法为: #{user.name}

      数据表字段与实体类属性不同时:

      当数据库的字段名和实体类的成员变量名不一样时(当然最好一样),可以使用两种方式实现查询结果正常映射到实体类:

      • sql语句中修改:在如: select * id as myId,name as myName,grade as myGrade from

        subjects

      • 配置标签修改:可以通过标签修改:(开发效率稍高)

        
          
              
              
              
              
              
              
          
          
          
          
        
        
      配置properties文件

      ​ 可以在resourese文件夹下创建properties文件,其中有多个值,每个值为{key、Value}映射形式:name=sun。

      主配置.xml文件使用properties文件

      配置:通过在 Configuration 主标签下添加 标签,然后写 属性resource=" ''。其指为相对地址,如果放在resource下,则直接写文件名.properties。如:

      使用:然后在后面的子标签如中的子标签,即可直接使用${key}来获得properties文件中的value的值。


      mybatis 连接池配置

      三种配置方式

      • 主配置文件中的标签

        属性type的取值:

        • ①POOLED :采用传统的javax.sql.DataSource规范(接口)中的连接池,mybatis有对其的实现类。

        • ②UNPOOLED :也是有实现了上述规范的实现类,但是没有连接池概念。连接是用时生成,关闭就真正销毁了。

        • ③ JNDI :采用服务器提供的 JDNI 技术实现,来获取DataSource对象,不同服务器能拿到的DataSource不一样。如 tomcat服务器采用dbcp连接池。

          注意:如果不是web或maven的war工厂,不能使用。

      POOLED取连接过程:

      概述:mybatis使用PooledDataSource 类来实现 java 的DataSource 规范,其内部步骤大概如下:

      • ①要新建连接时,看空闲池里是否有连接,若有,则取出一个返回。
      • ②若空闲池没有连接,则查看活跃池,若活跃池中连接数未达到最大数量,则新建一个连接,并放入活跃池,并返回。
      • ③若活跃池已达到最大数量,则从其中选择最先进来的(最老的)那个连接,进行适当加工,并返回。

      Sql语句和映射配置文件

      说明:查询语句有条件时,可以用配置文件中的标签进行辅助完成。

      映射配置文件中:

      下述都是在 select s.*,u.name as uName,u.sex,u.address,u.city from subjects s,user u where u.id=s.uid;

      一对多:

      说明:如一个学生,有多门学科成绩。

      resultMap中标签:

        • 属性:ofType:填写集合中的元素类型,如list中的E。

      dao映射配置文件

          
              
              
              
              
              
              
              
              
              
                  
                  
                  
                  
                  
              
          
          
          
      

      注意:user是主表,subjects是从表。

      多对多

      项目:ubuntu -- eclipse -- mybatisMany4Many

      说明:如一个用户,其可以多个爱好。而多个人,可以有同一个爱好。如此,用户库和爱好库则是多对多。

      sql语句:(两表为例)

      • 首先需要一个中间表user-enjoy,其数据项为:另两表的主键(如id)。
      • 然后是sql语句形如:Select u.*,e.* from user u Left outer join user-enjoy ue on u.id=ue.uid LEFT OUTER JOIN enjoy e on e.id=ue.eid;

      注意:编写sql语句中,多次将最后的条件子句中,别名写成了原名,这会报错。

      下述代码:User 和 Role类,多对多查询。Role的dao接口省略。

          
              
              
              
              
              
              
              
              
              
                  
                  
                  
              
          
          
          
          
          
      

      JNDI

      ​ JNDI(Java Naming and Directory Interface,java命名和目录接口),是sun公司提供的一种标准的java命名系统接口。

      ​ 说明:其打包方式为war(web archive),用于网页应用。具体还是要找专门资料


      mybatis缓存

      延迟加载

      问题:在一对多中,当我们有一个用户,他有100个账户,在查询用户的时候,要不要把关联的账户查出来?查询账户时,要不要把关联用户查出来?

      • 查用户时:应该延迟加载,即真正使用账户数据是才加载,不用的时候不查询。延迟加载也叫按需加载或懒加载。
      • 查账户时:可以立即加载。即不管用不用,只要一调用方法,马上发起查询。

      四种表关系

      一对多、多对多:通常使用延迟加载。

      一对一、多对一:通常使用立即加载。

      实现方式:无论是一对多还是多对一,其实都类似,就是在中的中,引用其他映射配置文件的 select * from subjects;

    X对多:

    实现:与X对一类似,区别:

    • 变成,及其内部的属性:javaType变成ofType
    • column="id",且在从表中,应该要有对应的字段,标识该数据属于主表中指定id的。
    • 还有一些就是SQL语句,和两个实体类结构的小问题了。

    缓存

    说明:内存用作数据库数据的缓存

    适用:

    • 经常查询且不经常改变的
    • 数据正确与否对最终结果影响不大的

    不适用:

    • 经常改变的数据(不可以使用写回的策略么?不行,因为可能存在多个线程或多台主机同时操作)
    • 数据的正确与否对最终结果影响很大的。(如:商品的库存、银行的汇率、股市的牌价)

    mybatis中的一级、二级缓存

    一级缓存:

    说明:SqlSession对象里的缓存。(一级缓存是默认开启,不需要手动设置)

    SqlSession在用户查询后,会缓存结果,形成一个Map结构,当再次查询同一个对象时,mybatis会先去查看是否SqlSession中是否有对应的值,如果有则返回,不需要再次访问数据库。

    清空:

    SqlSession.clearCache()

    或者关闭sqlSession之后,再新建:ss=SqlSessionFactory.openSession();

    SqlSession自动清空:

    时机:当调用SqlSession的commit、update、insert的时候,会自动清空缓存。

    注意:由于其机制,可能会导致缓存没有及时更新。如果并没有用该SqlSession进行更新、提交等操作,比如:

    • user = daoProxy.findbyone();
    • 暂停,并手动修改数据库信息。
    • user2 = daoProxy.findbyone();
    • user == user2 结果是true。因为他不会去访问数据库,user2是从缓存中提取的旧结果,并不是最新的数据。

    二级缓存:

    说明:SqlSessionFactory对象里的缓存。(需要手动设置开启)

    SqlSessionFactory有一个公共缓存区,可以被其生成的任何SqlSession共享。只要一个SqlSession进行了Commit,就会将如查询得到的信息缓存在SqlSessionFactory内部。

    当其新建或已建的SqlSession进行同样的查询时,就可以从其内部取数据,而不需要访问数据库。

    开启:

    默认是开启,设置地方:

    • 主配置文件:

      
        
      
      
    • 使用dao映射配置文件.xml:

      
      
      
      
      
    • 使用注解配置映射:

      ​ 注解dao接口,@CacheNamespace(blocking=true)即可。

    注意

    • 得到的实体类对象的值是一样的,但是不同的对象,如obj1==obj2:false。
    • (*)只有其他SqlSession提交或关闭之后,才会缓存到SqlSessionFactory中,若没有提交,则没有缓存。

    mybatis注解开发

    说明:注解与映射文件配置(路径同于dao接口),仅可以存在一个。

    大致开发步骤与普通一样,仅是主配置文件中,配置的是dao接口的地址。

    然后在dao接口中,注释方法,并传入sql语句。即可。

    项目:ubuntu--eclipse--mybatisAnnotation

    关于@Results、@ResultMap:

    • 当实体类名和数据库名不同时,如果不在sql中用别名,则需要配置映射,如下。

      @Select("select * from user;")
        @Results(id="userMap",value= {
                @Result(id=true,property="uid",column="id"),
                @Result(property="name",column="name"),
                @Result(property="sex",column="sex"),
                @Result(property="address",column="address"),
                @Result(property="city",column="city")
        })
         public List findAll();
      
    注解:
    • @Results:两个属性:id:唯一标识该Results; value:包含多个@Result注解,每个注解对应一个字段的映射。
    • @ResultMap:用于其他方法注解,引用一个@Results。其值标准格式为:value={"id1",”id2”...}

    多表查询注解

    多对一:

    @Results()中,加入属性:@Result(property="",column="", one=@One(select="path.to.method", fetchType=FetchType.EAGER))

    下面代码是UserDAOInterfaceUser实体类中包含成员变量Role role

    @Select("select u.*,ur.roleId from user as u LEFT JOIN user_role as ur on u.id=ur.userId;")
        @Results(id="userMap",value= {
                @Result(id=true,property="uid",column="id"),
                @Result(property="name",column="name"),
                @Result(property="sex",column="sex"),
                @Result(property="address",column="address"),
                @Result(property="city",column="city"),
                @Result(property="role",column="roleId",javaType=Role.class,
                    one=@One(select="com.yinghuo.mybatisAnnotation.                                                   dao.RoleDAOInterface.findOneById", fetchType=FetchType.EAGER)
                        )     
        })
    public List findAll();
    
    多对多:

    User实体类中包含成员变量List role

    说明:在userDAOInterface中与多对一类似,区别:

    • @Select()中语句变为:@Select("SELECT * from user;")

    • one=@One(...)变成many=@Many(...)

    • @One()中,method变成...findByUserId;其根据user_role表,和user提供的id,对应找到user_role中所有的数据项,提取roleId返回role中的多个数据项。

    • 以下是RoleDAOInterface.findByUserId:

      @Select("SELECT r.* from role r,user_role ur where ur.userId = #{para} and ur.roleId=r.roleId;")
      List findByUserId(int userId);
      

    缓存

    说明:一级缓存是自动开启的。而二级缓存需要设置。


    log4J

    properties文档:(尽量放在resources下)

    log4j.rootLogger=debug, stdout, R
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    
    # Pattern to output the caller's file name and line number.
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
    
    log4j.appender.R=org.apache.log4j.RollingFileAppender
    log4j.appender.R.File=example.log
    
    log4j.appender.R.MaxFileSize=100KB
    # Keep one backup file
    log4j.appender.R.MaxBackupIndex=5
    
    log4j.appender.R.layout=org.apache.log4j.PatternLayout
    log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
    
    \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014
    \u7248\u6743\u58F0\u660E\uFF1A\u672C\u6587\u4E3ACSDN\u535A\u4E3B\u300C\u5C31\u662F\u4E8C\u4E8C\u4E8C\u4E8C\u5A77\u300D\u7684\u539F\u521B\u6587\u7AE0\uFF0C\u9075\u5FAA CC 4.0 BY-SA \u7248\u6743\u534F\u8BAE\uFF0C\u8F6C\u8F7D\u8BF7\u9644\u4E0A\u539F\u6587\u51FA\u5904\u94FE\u63A5\u53CA\u672C\u58F0\u660E\u3002
    \u539F\u6587\u94FE\u63A5\uFF1Ahttps://blog.csdn.net/qq_34474324/article/details/98874675
    

    错误收集

    Result Maps collection does not contain value for ...

    • 说明:在映射配置文件中,有一个映射配置文件的select标签中属性resultType写成了resultMap
    • 解决方式:检查所有的配置映射文件(通常上述错误描述for后面,会标出该错误发生的dao接口,然后去该接口配置文件看),看看是不是有写错的。

你可能感兴趣的:(Mybatis 学习)