zoukankan      html  css  js  c++  java
  • Mybatis——基本使用,Mapper动态代理

    Mybatis简介

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

    MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

    Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

    jdbc编程的问题&&Mybatis解决

    1、数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。

    解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。

    2、Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

    解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

    3、向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

    解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。

    4、对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

    解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

    Mybatis架构

    1、 mybatis配置

    • SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
    • mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。

    2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂

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

    4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

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

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

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

    一.Mybatis环境搭建

    1、导包

    2、书写主配置文件 

    <?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>
    	<!--读取jdbc.properties文件-->
    	<properties resource="jdbc.properties"/>
    
    
    	<!-- 别名 包以其子包下所有类   头字母大小都行-->
    	<typeAliases>
     		<!--<typeAlias type="com.itheima.mybatis.pojo.User" alias="User"/> -->
    		<package name="cn.x5456.domain"/>
    	</typeAliases>
    
    
    	<!-- 和spring整合后 environments配置将废除    -->
    	<environments default="development">
    		<environment id="development">
    			<!-- 使用jdbc事务管理 -->
    			<transactionManager type="JDBC" />
    			<!-- 数据库连接池 -->
    			<dataSource type="POOLED">
    				<property name="driver" value="${jdbc.driver}" />
    				<property name="url"
    					value="${jdbc.url}" />
    				<property name="username" value="${jdbc.username}" />
    				<property name="password" value="${jdbc.password}" />
    			</dataSource>
    		</environment>
    	</environments>
    	
    	<!-- Mapper的位置  Mapper.xml 写Sql语句的文件的位置 -->
    	<mappers>
     		<mapper resource="cn/x5456/domain/User.xml"/>
     		<!--<mapper resource="sqlmap/User.xml" class="" url=""/> -->
     		<!--<mapper class="com.itheima.mybatis.mapper.UserMapper" /> -->
     		<!--<mapper url="" /> -->
    		<!--<package name="cn.x5456.dao"/>-->
    	</mappers>
    </configuration>
    
    1 jdbc.driver=com.mysql.jdbc.Driver
    2 jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
    3 jdbc.username=root
    4 jdbc.password=710130520a
    jdbc.properties
    1 # Global logging configuration
    2 log4j.rootLogger=DEBUG, stdout
    3 # Console output...
    4 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    5 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    6 log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    log4j.properties

    3、创建实体和sql映射文件

     1 package cn.x5456.domain;
     2 
     3 import java.io.Serializable;
     4 import java.util.Date;
     5 
     6 public class User implements Serializable {
     7     /**
     8      * 
     9      */
    10     private static final long serialVersionUID = 1L;
    11     private Integer id;
    12     private String username;// 用户姓名
    13     private String sex;// 性别
    14     private Date birthday;// 生日
    15     private String address;// 地址
    16 
    17 
    18     public Integer getId() {
    19         return id;
    20     }
    21     public void setId(Integer id) {
    22         this.id = id;
    23     }
    24     public String getUsername() {
    25         return username;
    26     }
    27     public void setUsername(String username) {
    28         this.username = username;
    29     }
    30     public String getSex() {
    31         return sex;
    32     }
    33     public void setSex(String sex) {
    34         this.sex = sex;
    35     }
    36     public Date getBirthday() {
    37         return birthday;
    38     }
    39     public void setBirthday(Date birthday) {
    40         this.birthday = birthday;
    41     }
    42     public String getAddress() {
    43         return address;
    44     }
    45     public void setAddress(String address) {
    46         this.address = address;
    47     }
    48     @Override
    49     public String toString() {
    50         return "User [id=" + id + ", username=" + username + ", sex=" + sex
    51                 + ", birthday=" + birthday + ", address=" + address + "]";
    52     }
    53 }
    user实体
    <?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.x5456.mapperper.UserMapper">-->
    <mapper namespace="jbb">
    
    <!--SQL代码-->
    	
    </mapper>
    

    4、增删改查操作

    1)通过ID查询一个用户

    <!-- 通过ID查询一个用户 -->
    <select id="findUserById" parameterType="Integer" resultType="cn.x5456.domain.User">
    	select * from user where id = #{id}
    </select>
    

    测试类

    @Test
    public void func() throws IOException {
    
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3.获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // -------------------------------------------
    
        // 4.执行相应的SQL语句
        User user = sqlSession.selectOne("jbb.findUserById", 1);
    
        System.out.println(user);
    
        // -------------------------------------------
    }
    

    2)根据用户名称模糊查询用户列表

    <!--
    	根据用户名称模糊查询用户列表
    	#{}    select * from user where id = ?    占位符  ? ==  '五'
    	${value}    select * from user where username like '%五%'  字符串拼接
     -->
    <select id="findUserByUsername" parameterType="String" resultType="cn.x5456.domain.User">	<!--返回的虽然是一个集合,但这里填的是泛型-->
    -- 		select * from user where username like "%${value}%"		由于它是直接拼接字符串的,所以会有SQL注入攻击的隐患
    	select * from user where username like "%"#{v}"%"
    </select>

    测试类

    public void func2() throws IOException {
    
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3.获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // -------------------------------------------
    
        // 4.执行相应的SQL语句
        List<User> list = sqlSession.selectList("jbb.findUserByUsername", "计");
    
        System.out.println(list);
    
        // -------------------------------------------
    }

    3)添加用户

    <insert id="insertUser" parameterType="cn.x5456.domain.User">
    	<selectKey keyProperty="id" resultType="Integer" order="AFTER">  <!--主键返回,auto_increment自增主键是在数据保存后,在添加的id;所以order是AFTER-->
    		select LAST_INSERT_ID()
    	</selectKey>
    	insert into user (username,birthday,address,sex) values (#{username},#{birthday},#{address},#{sex})
    </insert>
    

    测试类

    public void func3() throws IOException {
    
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3.获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // -------------------------------------------
    
        // 4.执行相应的SQL语句
        User user = new User();
        user.setUsername("jzy");
        user.setBirthday(new Date());
        user.setAddress("sadfsafsafs");
        user.setSex("男");
    
        int row = sqlSession.insert("jbb.insertUser",user);     // 返回受影响行数
    
        // 5.提交事务
        sqlSession.commit();
    
        System.out.println(row);
        System.out.println(user.getId());
    
        // -------------------------------------------
    }
    

    4)更新

    <update id="updateUserById" parameterType="cn.x5456.domain.User">
    	update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}
    </update>

    测试类

    @Test
    public void func4() throws IOException {
    
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3.获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // -------------------------------------------
    
        // 4.执行相应的SQL语句
        User user = new User();
        user.setId(3);
        user.setUsername("jbb");
        user.setBirthday(new Date());
        user.setAddress("dsb");
        user.setSex("nv");
    
        int row = sqlSession.update("jbb.updateUserById",user);     // 返回受影响行数
    
        // 5.提交事务
        sqlSession.commit();
    
        System.out.println(row);
    
        // -------------------------------------------
    }

    5)删除

    <delete id="deleteUserById" parameterType="cn.x5456.domain.User">
    	delete from user where id=#{id}
    </delete>

    测试类

    public void func5() throws IOException {
    
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3.获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // -------------------------------------------
    
        // 4.执行相应的SQL语句
        sqlSession.delete("jbb.deleteUserById",4);
    
        // 5.提交事务
        sqlSession.commit();
    
        // -------------------------------------------
    }
    

    #{}和${}

    #{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。

    ${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。

    parameterType和resultType

    parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。

    resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List中

    selectOne和selectList

    selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常

    selectList可以查询一条或多条记录。

    mybatis与hibernate不同

    Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。

    Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。

    Hibernate对象/关系映射能力强,数据库无关性好(MySQL切到Oracle无压力),对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。

    总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。 

    二.Dao开发方法

    1、原始Dao开发

    UserDao.java

    public interface UserDao {
    
        User findUserById(Integer id);
    
    }
    

    UserDaoImpl.java

    public class UserDaoImpl implements UserDao {
    
        private SqlSessionFactory sqlSessionFactory;    // 模拟Spring注入
    
        public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {   // 使用构造注入
            this.sqlSessionFactory = sqlSessionFactory;
        }
    
        @Override
        public User findUserById(Integer id) {
    
            SqlSession sqlSession = sqlSessionFactory.openSession();
    
            User user = sqlSession.selectOne("jbb.findUserById", id);
    
            return user;
        }
    }
    

    测试类

    private SqlSessionFactory sqlSessionFactory;
    
    @Before
    public void func6() throws IOException {
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    }
    
    @Test
    public void func7() {
        UserDaoImpl userDao = new UserDaoImpl(sqlSessionFactory);
    
        User userById = userDao.findUserById(3);
    
        System.out.println(userById);
    }
    

    注:如果接口中方法多的话,那么会有较多的重复代码;于是Mybatis给我们整了个动态代理

    2、Mapper动态代理方式

    四大条件

    • 1. namespace必须和Mapper接口类路径一致
    • 2. id必须和Mapper接口方法名一致
    • 3. parameterType必须和接口方法参数类型一致
    • 4. resultType必须和接口方法返回值类型一致

    修改配置文件中的namespace

    UserDao.java

    public interface UserDao {
    
        User findUserById(Integer id);
    
    }
    

    测试类

    public void func8() throws IOException {
    
        // 1.加载配置文件
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 2.创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 3.获取sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // -------------------------------------------
    
        // 4.SqlSEssion帮我生成一个实现类(给接口的字节码文件)
        UserDao mapper = sqlSession.getMapper(UserDao.class);
    
        User user = mapper.findUserById(1);
    
        System.out.println(user);
    
        // -------------------------------------------
    
    }
    

    三.SqlMapConfig.xml配置文件

     

  • 相关阅读:
    「UVA12293」 Box Game
    「CF803C」 Maximal GCD
    「CF525D」Arthur and Walls
    「CF442C」 Artem and Array
    LeetCode lcci 16.03 交点
    LeetCode 1305 两棵二叉搜索树中的所有元素
    LeetCode 1040 移动石子直到连续 II
    LeetCode 664 奇怪的打印机
    iOS UIPageViewController系统方法崩溃修复
    LeetCode 334 递增的三元子序列
  • 原文地址:https://www.cnblogs.com/x54256/p/8536118.html
Copyright © 2011-2022 走看看