0


SSM框架专题-MyBatis框架从零入门老杜版笔记(上)

MyBatis框架从零入门老杜版笔记(上)

一、MyBatis概述

1.1 框架 framework

在这里插入图片描述

1.2 MyBatis和JDBC的关系

MyBatis是增强版的jdbc,把一些功能通过反射封装好了

1.3 JDBC不足

  • sql语句写死,如果拓展功能,可能整个sql语句作废
  • set操作繁琐,如果sql语句有一百个?可能要写一百个set

1.4 了解MyBatis

  • MyBatis是对jdbc的封装
  • MyBatis之前叫做ibatis,后来才改名
  • MyBatis是持久层框架

1.5 了解ORM

  • O(Object):JVM中的java对象
  • R(Relational):关系型数据库
  • M(Mapping):映射
  • 什么是ORM?:JavaBean与数据表记录的互相映射
  • 在这里插入图片描述

二、MyBatis入门程序

2.1 建数据库表

在这里插入图片描述
添加两条字段
在这里插入图片描述

2.2 加载mybatis的五个步骤

  1. 配置Maven环境,打包方式设置为jar,加载MyBatis,MySQL
<packaging>jar</packaging><dependencies><!--  MyBatis  --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.11</version></dependency><!--  MySQL  --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency></dependencies>
  1. 新建,编辑mybatis-config.xml文件(放入resources文件夹)
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEconfigurationPUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environmentsdefault="development"><environmentid="development"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/powernode"/><propertyname="username"value="xxxx"/><propertyname="password"value="xxxx"/></dataSource></environment></environments><mappers><mapperresource="CarMapper.xml"/></mappers></configuration>
  1. 新建,配置xxxMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="org.mybatis.example.BlogMapper"><insertid="">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,1003,"面包车",13.00,"2020-10-13","飞行汽车")
    </insert></mapper>
  1. 在xxxMapper中编写sql代码(在3中已完成)
  2. 把xxxMapper.txt文件路径放入mybatis-config.txt中(在2中已完成)
<mappers><mapperresource="CarMapper.xml"/></mappers>

2.3 MyBatis中的事务

在mybatis-config.xml中有一行为

<transactionManagertype="JDBC"/>

type类型可以写成两种,一种是JDBC另一种是MANAGED(不区分大小写)

  • JDBC:交给JDBC处理事务(默认false,表示开启事务,需要手动提交)
  • MANAGED:有用到spring框架时设置为此,表交给框架处理事务,如果没有用到框架设置为此类型,则没有人处理事务,会自动提交

注意事项:

  • SqlSessionFactory.openSession()默认开启事务

2.4 编写MyBatis代码

SqlSession sqlSession =null;try{SqlSessionFactoryBuilder sqlSessionFactoryBuilder =newSqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
       sqlSession = sqlSessionFactory.openSession();int count = sqlSession.insert("insertCar");System.out.println("新增了记录"+count);//提交事务
       sqlSession.commit();}catch(Exception e){if(sqlSession !=null){
           sqlSession.rollback();}
       e.printStackTrace();}finally{if(sqlSession !=null){
           sqlSession.close();}}

2.5 在MyBatis中引入JUnit

在maven中添加junit依赖

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency>

新建test包,以及创建CarMapperTest类
在这里插入图片描述
在新建的类中编写MyBatis代码

publicclassCarMapperTest{@TestpublicvoidinsertCar(){SqlSession sqlSession =null;try{SqlSessionFactoryBuilder sqlSessionFactoryBuilder =newSqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
            sqlSession = sqlSessionFactory.openSession();int count = sqlSession.insert("insertCar");System.out.println("新增了记录"+count);//提交事务
            sqlSession.commit();}catch(Exception e){if(sqlSession !=null){
                sqlSession.rollback();}
            e.printStackTrace();}finally{if(sqlSession !=null){
                sqlSession.close();}}}}

运行,测试一下,绿色就是没问题
在这里插入图片描述
junit小知识点:

  • 断言测试方法:Assert.assertEquals(期望值,实际值);

2.6 MyBatis集成日志组件

  • SLF4J沙拉疯):logback就是继承沙拉疯实现的
  • LOG4J
  • LOG4J2
  • STDOUT_LOGGING:MyBatis内部实现的日志组件,

logback、log4j、log4j2是同一个作者编写的

若使用STDOUT_LOGGING,需要在mybatis-config.xml里添加配置文件
注意:settings标签必须添加在configuration下面

<configuration><settings><settingname="logImpl"value="STDOUT_LOGGING"/></settings></configuration>

这边不使用STDOUT_LOGGING日志组件,我们使用最常用的logback组件
配置步骤:

  1. 在maven中添加logback的依赖
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version><scope>test</scope></dependency>
  1. 在mybatis-config.xml中删除日志设置(除了STDOUT_LOGGING自带日志组件外,其他组件不需要写日志设置)
<!-- 删除这些 --><settings><settingname="logImpl"value="STDOUT_LOGGING"/></settings>
  1. 写配置文件(名字必须为logback.xml或logback-test.xml) 必须放在resources目录下
<?xml version="1.0" encoding="UTF-8"?><!-- 配置文件修改时重新加载,默认true --><configurationdebug="false"><!-- 控制台输出 --><appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder"charset="UTF-8"><!-- 输出日志记录格式 --><pattern>[%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!--    mybatis log configure--><loggername="com.apache.ibatis"level="TRACE"/><loggername="java.sql.Connection"level="DEBUG"/><loggername="java.sql.Statement"level="DEBUG"/><loggername="java.sql.PreparedStatement"level="DEBUG"/><!-- 日志输出级别,LOGBACK日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR--><rootlevel="DEBUG"><appender-refref="STDOUT"/><appender-refref="FILE"/></root></configuration>
  1. 运行测试

2.7 MyBatis工具类的编写

在编写代码中,每次都通过SqlSessionFactoryBuilder三步骤获取session太麻烦,我们编写一个工具类get会比较方便点

  1. 新建类utils.SqlSessionUtil在这里插入图片描述
  2. 编写代码
publicclassSqlSessionUtilTest{/**
     * 测试工具类
     */@TestpublicvoidopenSessionTest(){SqlSession session =SqlSessionUtil.openSession();int count = session.insert("insertCar");System.out.println(count);
        session.commit();
        
        session.close();}}
  1. 运行测试(数据库正常添加,完成)

三、使用MyBatis完成CRUD

3.1 实现Insert新增

1.1 MyBatis中完成Map集合传参

第一个入门程序已完成,现在我们来做MyBatis的CRUD操作
入门程序有个问题:实战的时候新增数据表行数据不可能是固定值
回顾:

<mappernamespace="org.mybatis.example.BlogMapper"><insertid="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,1003,"面包车",13.00,"2020-10-13","飞行汽车")
    </insert></mapper>
  1. 继续完善这个程序,新建一个模块叫CRUD,如图,把模块1的一些文件拷贝过来,maven依赖引用在这里插入图片描述
  2. 修改CarMapper.xml文件,加上占位符 在MyBatis中占位符为#{},对应JDBC的?
<insertid="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{},#{},#{},#{},#{})
    </insert>
  1. 编写测试代码
publicclassCarMapperTest{@TestpublicvoidinsertCar(){SqlSession session =SqlSessionUtil.openSession();Map<String,Object> map =newHashMap<>();
        map.put("carNum","1004");
        map.put("brand","比亚迪汉");
        map.put("guidePrice",160.00);
        map.put("produceTime","2022-06-08");
        map.put("carType","新能源汽车");int count= session.insert("insertCar", map);System.out.println("新增的条目:"+count);
        session.commit();

        session.close();}}
  1. 修改mapper文件中的占位符#{}(大括号中间填入map.get(key)里面的key)
<mappernamespace="org.mybatis.example.BlogMapper"><insertid="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert></mapper>
  1. 测试,完成

1.2 MyBatis中通过POJO类完成传参

  1. 新建POJO包、类(pojo.Car)
publicclassCar{privateLong id;privateString carNum;privateString brand;privateDouble guidePrice;privateString produceTime;privateString carType;//此处忽略构造方法、getting setting方法.....}
  1. 编写测试代码
@TestpublicvoidinsertCar(){SqlSession session =SqlSessionUtil.openSession();Car car =newCar(null,"1005","比亚迪秦",30.0,"2020-10-20","新能源");int count= session.insert("insertCar", car);System.out.println("新增的条目:"+count);
        session.commit();

        session.close();}
  1. 编辑mapper文件
<mappernamespace="org.mybatis.example.BlogMapper"><insertid="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert></mapper>
  1. 运行测试方法,总结: mapper文件中填写的#{carType}),MyBatis会通过反射机制查找getCarType()方法得到值

3.3 实现Delete删除

  1. 新增Mapper删除
<deleteid="deleteById">
        delete from t_car where id = #{id}
    </delete>
  1. 编写测试方法
@TestpublicvoiddeleteById(){SqlSession session =SqlSessionUtil.openSession();
        session.delete("deleteById",17);
        session.commit();
        session.close();}
  1. 注意事项 当参数只有一个时,参数名可以随意,但是尽量做到见名知意

3.4 实现update修改

  1. 编写mapper
<updateid="updateById">
        update t_car 
          set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
        where id=#{id}
    </update>
  1. 编写测试类
@TestpublicvoidtestUpdateById(){SqlSession session =SqlSessionUtil.openSession();Car car =newCar(3L,"5123","哈哈车",1.5,"2011-01-04","新技术");int count = session.update("updateById", car);System.out.println(count);
        session.commit();
        session.close();}

3.5 实现select查询

1.1 selectOne查询一个

  1. 编写Mapper文件
<selectid="selectOneById"resultType="com.powernode.mybatis.pojo.Car">
        select car_num as carNum,
               brand,guide_price as guidePrice,
               guide_price as guidePrice,
               produce_time as produceTime,
               car_type as carType
        from t_car
        where id = #{id}
    </select>
  1. 编写测试方法
@TestpublicvoidselectOneById(){SqlSession session =SqlSessionUtil.openSession();Car car = session.selectOne("selectOneById",16L);System.out.println(car);
        session.close();}

注意事项:

  • Mapper配置文件里要增加:resultType=“com.powernode.mybatis.pojo.Car”,指定查询出来是什么类,MyBatis会自动转换成该类
  • guide_price as guidePrice,取别名是为了让pojo类属性查询结果对应上符合ORM

1.2 selectList查询所有,返回一个集合

  1. 编写Mapper配置文件
<selectid="selectAll"resultType="com.powernode.mybatis.pojo.Car">
        select car_num as carNum,
               brand,guide_price as guidePrice,
               guide_price as guidePrice,
               produce_time as produceTime,
               car_type as carType
        from t_car
    </select>
  1. 编写测试代码
@TestpublicvoidselectAll(){SqlSession session =SqlSessionUtil.openSession();List<Car> cars = session.selectList("selectAll");
        cars.forEach( car ->System.out.println(car));
        session.close();}

3.6 Mapper映射文件的namespace

在mapper.xml文件中有一个namespasce,这个属性是用来指定命名空间的,用来防止id重复
在java程序中的写法:

List<Car> cars = session.selectList("namespace.selectAll");

这样写才是严谨、完整的

四、MyBatis核心配置文件详解

4.1 environment标签

一个environment对应一个SqlSessionFactory
一个SqlSessionFactory对应一个数据库

  1. 多环境下,配置文件这样写(两个environment)
<environmentsdefault="development"><!-- 一个environment对应一个数据库 --><environmentid="development"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/powernode"/><propertyname="username"value="root"/><propertyname="password"value="root"/></dataSource></environment><environmentid="development2"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/powernode2"/><propertyname="username"value="root"/><propertyname="password"value="root"/></dataSource></environment></environments>
  1. 写测试方法
publicclass testConfiguration {@TestpublicvoidtestEnvironment()throwsIOException{SqlSessionFactoryBuilder sessionFactoryBuilder =newSqlSessionFactoryBuilder();SqlSessionFactory sessionFactory = sessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"),"development2");SqlSession session = sessionFactory.openSession();int count = session.insert("insertCar");System.out.println(count);
        session.commit();
        session.close();}}

4.2 transactionManager标签

在mybatis-config.xml配置文件中,有transactionManager子标签,表示设置MyBatis的事务管理器

<transactionManagertype="JDBC"/>

MyBatis中有两个事务管理器

  • JDBC:交给原生JDBC管理setAutoComit(false);需要手动comit()提交
  • MANAGED:交给JEE处理,一般由JEE框架处理,如Spring

MyBatis底层有一个Transaction接口,实现两个事务

  • 如果type=“JDBC”,那么底层会实例化JdbcTransaciton对象
  • 如果type=“MANAGED”,那么底层会实例化ManagedTransaciton

4.3 dataSource标签

<dataSourcetype="POOLED">
    .......
</dataSource>

问:这个标签有啥用
答:dataSource表示数据源,用来获取Connection对象

它的type属性可以填写三个值:

  • UNPOOLED:不使用数据库连接池,每次获取Connection都要创建一个新对象
  • POOLED:使用MyBatis自带的连接池
  • JNDI:连接第三方的数据源(如果自己手写框架也可以用这个)

3.1 type属性详解

不同类型的数据源下有不同的属性,比如

<dataSourcetype="POOLED">
    ....
    <properties/>
    ....
</dataSource>

<dataSourcetype="JNDI">
    ....
    <properties/>
    ....
</dataSource>

<properties/>

数量、内容是不一样的,具体需要看官方文档
JDNI是:java命名目录接口,Tomcat服务器实现了这个规范
假如需要用第三方数据源,Tomcat中实现了这个数据源,则在properties中填写对应的数据,要和tomat对应上

3.2 pooled和unpooled的区别

  1. unpooled表示不使用连接池,每次请求过来都会创建一个Connection
  2. pooled表示使用MyBatis自带的连接池:请求过来会先从连接池获取Connection对象

:使用连接池有什么好处?

3. 迟内的Connection数量是固定的,比如池子大小是5,如果5个连接都被占用,第6个要获取连接就先等待,数量固定
4. 假如有人一直F5刷新,没有用连接池的话,就会一直创建Connection对象,如果实例化对象过多,可能会导致服务器宕机,数量固定
5. 有新请求,第一反应去池中查找,可以增加效率

3.3 配置具体的数据库连接池参数

<!--连接池最大连接数,默认:10--><propertyname="poolMaximumActiveConnections "value="10"/><!--可以同时存在的最大空闲连接数,空闲太多则真正关闭一些Connection--><propertyname="poolMaximumIdleConnections "value="5"/><!--超时强制关闭时间,默认20000--><propertyname="poolMaximumCheckoutTime "value="20000"/><!--如果连接花费时间很长,连接池会隔断时间尝试重新连接并打印日志--><propertyname="poolTimeToWait "value="2000"/>

4.4 properties标签

1. 第一种写法(写在配置文件内)

在configuration标签下有一个properties子标签,是用来设置变量的

<propertyname="key"value="value"/>

这样写相当于往map集合里放入一个key,可以通过key拿到value,在mybatis-config.xml的其他地方,可以写成${key}获取到value

2. 第二种写法(外部文件-相对路径)

<propertiesresource="jdbc.properties"/>

然后在外部新建一个jdbc.properties,如(等号左边是key右边是value)

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/powernode
username=root
password=root

3. 第三种写法(外部文件-绝对路径)

不推荐这种写法

<propertiesurl="file:///D://jdbc.properties"/>

五、在Web中应用MyBatis(使用MVC架构模式)

5.1 环境搭建

建立如下数据库:
在这里插入图片描述

5.2 idea环境搭建

1. idea新建一个maven项目,Create from archetype勾选上,选择如图所示webapp

在这里插入图片描述

2. maven依赖配置好

<dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><dependency><groupId>servletapi</groupId><artifactId>servletapi</artifactId><version>2.4-20040521</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.11</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency></dependencies>

3. 修改web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?><web-appxmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"metadata-complete="true"></web-app>

4. 编写mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEconfigurationPUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><propertiesresource="jdbc.properties"/><environmentsdefault="development"><!-- 一个environment对应一个数据库 --><environmentid="development"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="${driver}"/><propertyname="url"value="${url}"/><propertyname="username"value="${username}"/><propertyname="password"value="${password}"/></dataSource></environment><environmentid="development2"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/powernode2"/><propertyname="username"value="root"/><propertyname="password"value="root"/></dataSource></environment></environments><mappers><mapperresource="ActMapper.xml"/></mappers></configuration>

5. 在resources目录下新建、编辑jdbc.properties文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/powernode
username=root
password=root

6. 在resources目录下新建、编写Mapper文件

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="account"><insertid="insertAct">
        insert into t_act(id,name,balance)
        values (null,#{name},#{balance})
    </insert></mapper>

7. 在resources目录下新建、编写logback.xml配置文件

<?xml version="1.0" encoding="UTF-8"?><!-- 配置文件修改时重新加载,默认true --><configurationdebug="false"><!-- 控制台输出 --><appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder"charset="UTF-8"><!-- 输出日志记录格式 --><pattern>{%thread} %-5level %logger{50} - %msg%n</pattern></encoder></appender><!--    mybatis log configure--><loggername="com.apache.ibatis"level="TRACE"/><loggername="java.sql.Connection"level="DEBUG"/><loggername="java.sql.Statement"level="DEBUG"/><loggername="java.sql.PreparedStatement"level="DEBUG"/><!-- 日志输出级别,LOGBACK日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR--><rootlevel="DEBUG"><appender-refref="STDOUT"/><appender-refref="FILE"/></root></configuration>

8. 新建pojo类,放到com.powernode.bank.pojo包中

publicclassAccount{privateLong id;privateString name;privateDouble balance;......//此处省略构造方法、getting setting toString方法,请自行创建

9. 新建dao, servce, utils, web,并在utils中新建SqlSessionUtil类

在这里插入图片描述

packagecom.powpernode.bank.utils;importorg.apache.ibatis.io.Resources;importorg.apache.ibatis.session.SqlSession;importorg.apache.ibatis.session.SqlSessionFactory;importorg.apache.ibatis.session.SqlSessionFactoryBuilder;importjava.io.IOException;/**
 * @author huochai
 * @date 2022/10/15 9:22
 */publicclassSqlSessionUtil{/**
     * 一个sessionFactory对应一个environment(数据库),所以不要每次运行都new一个新的
     *//**
     * 构造方法设置为私有的,防止被实例化
     */privateSqlSessionUtil(){}privatestaticSqlSessionFactory sessionFactory;static{try{
            sessionFactory =newSqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));}catch(IOException e){
            e.printStackTrace();}}publicstaticSqlSessionopenSession(){SqlSession session = sessionFactory.openSession();return session;}}

10. 在webapp中新建index.html

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>银行转账</title></head><body><formaction="/bank/transfer"method="post">
  转出账户:<inputtype="text"name="fromActno"><br>
  转入账户:<inputtype="text"name="toActno"><br>
  转账金额<inputtype="text"name="money"><br><inputtype="submit"value="转账"></form></body></html>

5.3 后端代码实现

根据前端请求发送的路线完善后端代码

1. 新建、完善AccountServlet(接口与实现类)

packagecom.powpernode.bank.web;importcom.powpernode.bank.exception.MoneyNotEnoughException;importcom.powpernode.bank.exception.TransferNotSuccessException;importcom.powpernode.bank.service.AccountService;importcom.powpernode.bank.service.impl.AccountServiceImpl;importjavax.jws.WebService;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/**
 * @author huochai
 * @date 2022/10/16 15:15
 */@WebServlet("/transfer")publicclassAccountServletextendsHttpServlet{/**
     * 调用业务类处理业务
     */privateAccountService accountService =newAccountServiceImpl();@OverrideprotectedvoiddoPost(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{//接收前端传来的参数String fromActno = request.getParameter("fromActno");String toActno = request.getParameter("toActno");Double money =Double.parseDouble(request.getParameter("money"));//调用业务类处理转账try{
            accountService.transfer(fromActno,toActno,money);
            response.sendRedirect(request.getContextPath()+"/success.html");}catch(MoneyNotEnoughException e){
            e.printStackTrace();
            response.sendRedirect(request.getContextPath()+"/error1.html");}catch(TransferNotSuccessException e){
            e.printStackTrace();
            response.sendRedirect(request.getContextPath()+"/error2.html");}}}

2. 完善业务层AccountService接口、实现类

packagecom.powpernode.bank.service;importcom.powpernode.bank.exception.MoneyNotEnoughException;importcom.powpernode.bank.exception.TransferNotSuccessException;/**
 * @author huochai
 * @date 2022/10/16 15:17
 */publicinterfaceAccountService{/**
     * 转账业务方法
     * @param fromAct 转出账户
     * @param toAct 转入账户
     * @param money 转账金额
     * @throws MoneyNotEnoughException 转出账户余额不足异常
     * @throws TransferNotSuccessException 转账失败异常
     */voidtransfer(String fromAct,String toAct,Double money)throwsMoneyNotEnoughException,TransferNotSuccessException;}
packagecom.powpernode.bank.service.impl;importcom.powpernode.bank.dao.AccountDao;importcom.powpernode.bank.dao.impl.AccountDaoImpl;importcom.powpernode.bank.exception.MoneyNotEnoughException;importcom.powpernode.bank.exception.TransferNotSuccessException;importcom.powpernode.bank.pojo.Account;importcom.powpernode.bank.service.AccountService;importjavax.jws.WebService;/**
 * @author huochai
 * @date 2022/10/16 15:19
 * 只负责处理业务逻辑,不涉及数据库修改
 */publicclassAccountServiceImplimplementsAccountService{privateAccountDao accountDao =newAccountDaoImpl();@Overridepublicvoidtransfer(String fromAct,String toAct,Double money)throwsMoneyNotEnoughException,TransferNotSuccessException{//判断余额是否充足Account fromAccount = accountDao.selectById(Long.parseLong(fromAct));if(fromAccount.getBalance()<money){thrownewMoneyNotEnoughException("对不起,余额不足");}//将fromAct减少money,toAct增加moneyAccount toAccount = accountDao.selectById(Long.parseLong(toAct));
        fromAccount.setBalance(fromAccount.getBalance()-money);
        toAccount.setBalance(toAccount.getBalance()+money);//更新两个账户int count = accountDao.updateAccount(fromAccount);
        count += accountDao.updateAccount(toAccount);if(count<2){thrownewTransferNotSuccessException("转账失败,未知错误");}}}

3. 完善DAO层,接口、实现类

packagecom.powpernode.bank.dao;importcom.powpernode.bank.pojo.Account;/**
 * @author huochai
 * @date 2022/10/16 15:20
 */publicinterfaceAccountDao{/**
     * 根据ID查询账户
     * @param id id
     * @return 返回账户
     */AccountselectById(Long id);/**
     * 更新用户
     * @param account 需要更新的用户
     * @return 1表示更新完成
     */intupdateAccount(Account account);/**
     * 插入用户
     * @param account 需要插入的用户
     * @return 1表示插入完成
     */intinsertAccount(Account account);}
packagecom.powpernode.bank.dao.impl;importcom.powpernode.bank.dao.AccountDao;importcom.powpernode.bank.pojo.Account;importcom.powpernode.bank.utils.SqlSessionUtil;importorg.apache.ibatis.session.SqlSession;/**
 * @author huochai
 * @date 2022/10/16 15:21
 */publicclassAccountDaoImplimplementsAccountDao{@OverridepublicAccountselectById(Long id){SqlSession session =SqlSessionUtil.openSession();Account account = session.selectOne("account.selectById", id);
        session.close();return account;}@OverridepublicintupdateAccount(Account account){SqlSession session =SqlSessionUtil.openSession();int count = session.update("account.updateById", account);
        session.commit();return count;}@OverridepublicintinsertAccount(Account account){SqlSession session =SqlSessionUtil.openSession();int count = session.insert("account.insertAct", account);
        session.commit();return count;}}

4. 完善两个异常类

/**
 * @author huochai
 * @date 2022/10/16 15:47
 */publicclassMoneyNotEnoughExceptionextendsException{publicMoneyNotEnoughException(){};publicMoneyNotEnoughException(String msg){super(msg);}}
packagecom.powpernode.bank.exception;/**
 * @author huochai
 * @date 2022/10/16 15:51
 */publicclassTransferNotSuccessExceptionextendsException{publicTransferNotSuccessException(){};publicTransferNotSuccessException(String msg){super(msg);}}

5. 当前项目结构

在这里插入图片描述

6. 完善事务处理

目前为止项目里存在一个问题,没有事务处理机制
如果在更新完账户1之后异常,就会出现少钱的现象
在这里插入图片描述

6.1 加入线程池

/**
* 完善后的SqlSessionUtil工具类
*/packagecom.powpernode.bank.utils;importorg.apache.ibatis.io.Resources;importorg.apache.ibatis.session.SqlSession;importorg.apache.ibatis.session.SqlSessionFactory;importorg.apache.ibatis.session.SqlSessionFactoryBuilder;importjava.io.IOException;/**
 * @author huochai
 * @date 2022/10/15 9:22
 */publicclassSqlSessionUtil{publicstaticThreadLocal<SqlSession> local =newThreadLocal();privateSqlSessionUtil(){}privatestaticSqlSessionFactory sessionFactory;static{try{
            sessionFactory =newSqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));}catch(IOException e){
            e.printStackTrace();}}publicstaticSqlSessionopenSession(){SqlSession session = local.get();if(session==null){
            session = sessionFactory.openSession();
            local.set(session);}return session;}publicstaticvoidclose(SqlSession session){if(session!=null){
            session.close();
            local.remove();}}}

6.2 在业务层控制事务处理

publicclassAccountServiceImplimplementsAccountService{privateAccountDao accountDao =newAccountDaoImpl();@Overridepublicvoidtransfer(String fromAct,String toAct,Double money)throwsMoneyNotEnoughException,TransferNotSuccessException{SqlSession session =SqlSessionUtil.openSession();//判断余额是否充足Account fromAccount = accountDao.selectById(Long.parseLong(fromAct));if(fromAccount.getBalance()<money){thrownewMoneyNotEnoughException("对不起,余额不足");}//将fromAct减少money,toAct增加moneyAccount toAccount = accountDao.selectById(Long.parseLong(toAct));
        fromAccount.setBalance(fromAccount.getBalance()-money);
        toAccount.setBalance(toAccount.getBalance()+money);//更新两个账户int count = accountDao.updateAccount(fromAccount);
        count += accountDao.updateAccount(toAccount);if(count<2){thrownewTransferNotSuccessException("转账失败,未知错误");}

        session.commit();SqlSessionUtil.close(session);}}

5.4 MyBatis作用域(Scope)和生命周期

名称生命周期SqlSessionFactoryBuilder只是用来buildFactory的,利用完即可丢弃SqlSessionFactory一个数据库对应一个Factory,最好不要丢弃SqlSession一个线程对应一个SqlSession
观察项目还有什么问题,发现DAO层代码很少并且很固定

publicclassAccountDaoImplimplementsAccountDao{//可以发现第一行都是获取Session,第二行执行,第三行return@OverridepublicAccountselectById(Long id){SqlSession session =SqlSessionUtil.openSession();Account account = session.selectOne("account.selectById", id);return account;}@OverridepublicintupdateAccount(Account account){SqlSession session =SqlSessionUtil.openSession();int count = session.update("account.updateById", account);return count;}@OverridepublicintinsertAccount(Account account){SqlSession session =SqlSessionUtil.openSession();int count = session.insert("account.insertAct", account);return count;}}

有没有什么框架可以代替这种简单重复的工作呢?
答:可以利用javassist框架
通过使用Javassist对字节码操作为JBoss实现动态“AOP”框架

5.5 使用javassist动态生成类

publicclassJavassistTest{@TestpublicvoidgenerateClass()throwsException{//获取类池ClassPool classPool =ClassPool.getDefault();// 制造类CtClass ctClass = classPool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");// 制造方法String method ="public void insert(){System.out.println(166);}";CtMethod make =CtMethod.make(method, ctClass);// 把方法加到类中
        ctClass.addMethod(make);//在内存中生成类
        ctClass.toClass();Class<?> aClass =Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");Object o = aClass.newInstance();Method insert = aClass.getDeclaredMethod("insert");
        
        insert.invoke(o);}}

5.6 使用javassist生成类并实现接口

@TestpublicvoidgenerateInterface()throwsException{//获取类池ClassPool pool =ClassPool.getDefault();//制造类CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");//制造接口CtClass ctClass1 = pool.makeInterface("com.powernode.javassist.test.AccountDao");//添加接口
        ctClass.addInterface(ctClass1);CtMethod make =CtMethod.make("public void delete(){System.out.println(\"Hello delete\");}", ctClass);
        ctClass.addMethod(make);//装载类Class<?> aClass = ctClass.toClass();AccountDao o =(AccountDao) aClass.newInstance();
        o.delete();}

5.7 使用javassist动态实现接口中所有方法

@TestpublicvoidgenerateInterfaceAll()throwsException{//获取类池ClassPool pool =ClassPool.getDefault();//制造类CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");//制造接口CtClass ctInterface = pool.makeInterface("com.powernode.javassist.test.AccountDao");//类实现接口
        ctClass.addInterface(ctInterface);Method[] declaredMethods =AccountDao.class.getDeclaredMethods();//制造方法//方法内容Arrays.stream(declaredMethods).forEach(method ->{try{StringBuffer methodValue =newStringBuffer();
                methodValue.append("public ");
                methodValue.append(method.getReturnType().getName()+" ");
                methodValue.append(method.getName()+"(");Class<?>[] parameterTypes = method.getParameterTypes();for(int i =0; i < parameterTypes.length; i++){
                    methodValue.append(parameterTypes[i].getName()+" ");
                    methodValue.append("args"+i);if(i!= parameterTypes.length-1){
                        methodValue.append(",");}}
                methodValue.append(")");
                methodValue.append("{System.out.println(\"hello all\");");if("void".equals(method.getReturnType().getName())){}elseif("int".equals(method.getReturnType().getName())){
                    methodValue.append("return 1;");}elseif("java.lang.String".equals(method.getReturnType().getName())){
                    methodValue.append("return \"666\";");}
                methodValue.append("}");CtMethod make =CtMethod.make(String.valueOf(methodValue), ctClass);
                ctClass.addMethod(make);}catch(CannotCompileException e){
                e.printStackTrace();}});Class<?> toClass = ctClass.toClass();AccountDao accountDao =(AccountDao) toClass.newInstance();
        accountDao.delete();
        accountDao.insert("666",50.00);
        accountDao.selectByActno("555");}

5.8 编写工具类GenerateDaoProxy(自动实现DAO层接口)

注意:若想利用GenerateDaoProxy,
则Mapper.xml文件的namespace必须为DAO层接口的全类名
sqlId必须为DAO层接口中的方法

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.powpernode.bank.dao.AccountDao"><insertid="insertAccount">
        insert into t_act(id,name,balance)
        values (null,#{name},#{balance})
    </insert><selectid="selectById"resultType="com.powpernode.bank.pojo.Account">
        select * from t_act where id=#{id}
    </select><updateid="updateAccount">
        update t_act set name=#{name},balance=#{balance} where id=#{id}
    </update></mapper>
packagecom.powpernode.bank.utils;importorg.apache.ibatis.javassist.CannotCompileException;importorg.apache.ibatis.javassist.ClassPool;importorg.apache.ibatis.javassist.CtClass;importorg.apache.ibatis.javassist.CtMethod;importorg.apache.ibatis.mapping.SqlCommandType;importorg.apache.ibatis.session.SqlSession;importjava.lang.reflect.Method;importjava.util.Arrays;/**
 * @author huochai
 * @date 2022/10/16 23:30
 */publicclassGenerateDaoProxy{/**
     * 这个工具类是框架的开发者提供的
     * 开发者可以给使用者规定传进哪些参数
     *
     * 传进接口,返回实现所有方法的类
     * @param daoInterface 接口
     * @return Impl类
     */publicstaticObjectgenerate(SqlSession session,Class daoInterface){//类池ClassPool pool =ClassPool.getDefault();//制造类CtClass ctClass = pool.makeClass(daoInterface.getName()+"Proxy");//制造接口CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        ctClass.addInterface(ctInterface);Method[] declaredMethods = daoInterface.getDeclaredMethods();Arrays.stream(declaredMethods).forEach(method ->{try{StringBuffer methodCode =newStringBuffer();//添加修饰符
                methodCode.append("public ");//添加返回值
                methodCode.append(method.getReturnType().getName()+" ");
                methodCode.append(method.getName());
                methodCode.append("(");Class<?>[] parameterTypes = method.getParameterTypes();for(int i =0; i < parameterTypes.length; i++){
                    methodCode.append(parameterTypes[i].getName()+" ");
                    methodCode.append("arg"+i);if(i!= parameterTypes.length-1){
                        methodCode.append(",");}}
                methodCode.append("){");/**
                 * 括号中间需要写对应的session.insert或session.select方法
                 */String sqlId = daoInterface.getName()+"."+method.getName();SqlCommandType sqlCommandType = session.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
                methodCode.append("org.apache.ibatis.session.SqlSession session = com.powpernode.bank.utils.SqlSessionUtil.openSession();");if(sqlCommandType ==SqlCommandType.INSERT){}if(sqlCommandType ==SqlCommandType.DELETE){}if(sqlCommandType ==SqlCommandType.UPDATE){
                    methodCode.append("return session.update(\""+sqlId+"\", arg0);");}if(sqlCommandType ==SqlCommandType.SELECT){String resultType = method.getReturnType().getName();
                    methodCode.append("return ("+resultType+")session.selectOne(\""+sqlId+"\", arg0);");}

                methodCode.append("}");System.out.println(methodCode.toString());CtMethod ctMethod =CtMethod.make(methodCode.toString(), ctClass);
                ctClass.addMethod(ctMethod);}catch(CannotCompileException e){
                e.printStackTrace();}});Object obj =null;try{Class<?> toClass = ctClass.toClass();
            obj = toClass.newInstance();}catch(Exception e){
            e.printStackTrace();}return obj;}}

最后在业务层引用Dao的时候改一下即可:

publicclassAccountServiceImplimplementsAccountService{privateAccountDao accountDao =(AccountDao)GenerateDaoProxy.generate(SqlSessionUtil.openSession(),AccountDao.class);.....

写完以后得知一个好消息,MyBatis已经实现了映射机制,不用自己手写代码了(要求和上面一样,对namespace以及sqlId有格式要求)

//自己写的privateAccountDao accountDao =(AccountDao)GenerateDaoProxy.generate(SqlSessionUtil.openSession(),AccountDao.class);//MyBatis自带privateAccountDao accountDao =SqlSessionUtil.openSession().getMapper(AccountDao.class);

5.9 面向接口的方式进行CRUD

新建maven模块mybatis-005-crud2
添加maven依赖

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>MyBatis</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><packaging>jar</packaging><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency></dependencies><groupId>com.powernode</groupId><artifactId>mybatis-005-crud2</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties></project>

添加mapper类

/**
 * @author huochai
 * @date 2022/10/17 9:38
 * 在MyBatis中,一般不叫XXXDao,一般叫xxxMapper
 */publicinterfaceCarMapper{/**
     * 插入数据
     * @param car
     * @return 返回1表示正常
     */intinsert(Car car);/**
     * 根据ID删除
     * @param id
     * @return 返回1表示正常
     */intdeleteById(Long id);/**
     * 更新数据
     * @param car
     * @return 返回1表示正常
     */intupdate(Car car);/**
     * 根据ID查询
     * @param id
     * @return 返回1表示正常
     */CarselectById(Long id);/**
     * 查询所有的数据
     * @return 返回给List集合
     */List<Car>selectAll();}

编写CarMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.powernode.mybatis.mapper.CarMapper"><insertid="insert">
      insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
      values(null, #{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert><deleteid="delete">
      delete from t_car where id=#{id}
    </delete><updateid="update">
      update t_car
      set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
      where id=#{id}
    </update><selectid="selectById"resultType="com.powernode.mybatis.pojo.Car">
      select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
      from t_car where id=#{id}
    </select><selectid="selectAll"resultType="com.powernode.mybatis.pojo.Car">
        select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
    </select></mapper>

新建工具类,SqlSessionUtil

publicclassSqlSessionUtil{publicstaticThreadLocal<SqlSession> local =newThreadLocal();privateSqlSessionUtil(){}privatestaticSqlSessionFactory sessionFactory;static{try{
            sessionFactory =newSqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));}catch(IOException e){
            e.printStackTrace();}}publicstaticSqlSessionopenSession(){SqlSession session = local.get();if(session==null){
            session = sessionFactory.openSession();
            local.set(session);}return session;}publicstaticvoidclose(SqlSession session){if(session!=null){
            session.close();
            local.remove();}}}

编写测试用例

publicclassTestMapper{@Testpublicvoidinsert(){SqlSession session =SqlSessionUtil.openSession();CarMapper mapper = session.getMapper(CarMapper.class);Car car =newCar(null,"5556","兰博基尼六",1952.1,"2060-02-06","跑车");
        mapper.insert(car);
        session.commit();}@Testpublicvoiddelete(){SqlSession session =SqlSessionUtil.openSession();CarMapper mapper = session.getMapper(CarMapper.class);
        mapper.deleteById(21L);
        session.commit();}@Testpublicvoidupdate(){SqlSession session =SqlSessionUtil.openSession();CarMapper mapper = session.getMapper(CarMapper.class);Car car =newCar(18L,"5556","兰博基尼六",1952.1,"2060-02-06","跑车");
        mapper.update(car);
        session.commit();}@TestpublicvoidselectById(){SqlSession session =SqlSessionUtil.openSession();CarMapper mapper = session.getMapper(CarMapper.class);Car car = mapper.selectById(18L);System.out.println(car);}@TestpublicvoidselectAll(){SqlSession session =SqlSessionUtil.openSession();CarMapper mapper = session.getMapper(CarMapper.class);List<Car> cars = mapper.selectAll();
        cars.forEach(car ->System.out.println(car));}}

六、MyBatis小技巧

6.1 #{}和${}的区别

  • **#{property}**:底层使用PreparedStatement。特点:先进行SQL语句的编译,然后给SQL语句的占位符?传值。可以避免SQL注入的风险
  • **${property}**:底层使用Statement。特点:先进行SQL语句的拼接,然后再对SQL语句进行编译。存在SQL注入的风险

6.2 什么时候使用${}

  1. 如果需要SQL语句的关键字放到SQL语句中,只能使用${},因为#{}是以值的形式放到SQL语句当中的。 例:
<mappernamespace="com.powernode.mybatis.mapper.CarMapper"><selectid="selectByType"resultType="com.powernode.mybatis.pojo.Car">
        select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
        order by produce_time ${ascOrDesc}
    </select></mapper>
publicclassCarMapperTest{@TestpublicvoidselectAllSortedTest(){SqlSession session =SqlSessionUtil.openSession();List<Car> cars = session.getMapper(CarMapper.class).selectByType("desc");
        cars.forEach( car ->System.out.println(car));
        session.close();}}
  1. 拼接表名的时候,比如需要记录日志信息,如果每天都往同一个日志表中存储数据,慢慢的表信息就会越来越多,可以采用加日期分类的方法,直接查询天表可以增加效率,如t_log_20221017这样分类(然后拼接表名就可以采用${}的方法)
  2. 批量删除,可以写,删除用户123、456、8/7
deletefrom t_car where id in(123,456,789)//用美元括号deletefrom t_car where id in(${})

6.3 查找包含某个关键词str的方法

  1. concat()函数
<select id="selectLikeSome" resultType="com.powernode.mybatis.pojo.Car">select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
        where car_type like Concat('%',#{str},'%')</select>
  1. 用双引号把通配符引出去,让#{}在外面好被jdbc检测到(常用)
<select id="selectLikeSome" resultType="com.powernode.mybatis.pojo.Car">select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
        where car_type like"%"#{str}"%"</select>

6.4 MyBatis中起别名

namespace不能起别名
所有别名不区分大小写

<typeAliases><!--给type起别名,可以直接用alias名称读取到--><typeAliastype="com.powernode.mybatis.pojo.Car"alias="car"/><!--省略alias,默认就是类简名,比如car--><typeAliastype="com.powernode.mybatis.pojo.Car"/><!--包下所有类自动起别名,不区分大小写--><packagename="com.powernode.mybatis.pojo"/></typeAliases>

6.5 MyBatis小技巧之Mapper的配置

<mappers><!--1. 根路径下查找CarMapper.xml文件--><mapperresource="CarMapper.xml"/><!--2. 绝对路径查找--><mapperurl="file://c:/CarMapper.xml"/><!--3. 查找映射接口同级目录下的Mapper.xml文件--><!--   com/powernode/mybatis/mapper 建包需要这样建--><mapperclass="com.powernode.mybatis.mapper.CarMapper"/><!--最常用:路径下自动查找接口对应名字xml文件--><packagename="com.powernode.mybatis.mapper"/></mappers>

6.6 插入数据时获取自动生成的主键

插入一条数据的时候,自动返回主键到制定属性中

useGeneratedKeys=“true” 表示使用自动生成的主键值
keyProperty=“id” 制定属性值赋值给对象的哪个属性

<insertid="insertCar"useGeneratedKeys="true"keyProperty="id">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
@TestpublicvoidinsertCar(){SqlSession session =SqlSessionUtil.openSession();Car car =newCar(null,"9851","比亚迪ao",30.0,"2020-10-20","新能源");
        session.getMapper(CarMapper.class).insertCar(car);System.out.println(car);
        session.close();}

结果:Car{id=22, carNum=‘9851’, brand=‘比亚迪ao’, guidePrice=30.0, produceTime=‘2020-10-20’, carType=‘新能源’}
把自动递增的主键返回给了id

标签: mybatis java mysql

本文转载自: https://blog.csdn.net/u011005040/article/details/127247600
版权归原作者 火柴哟 所有, 如有侵权,请联系我们删除。

“SSM框架专题-MyBatis框架从零入门老杜版笔记(上)”的评论:

还没有评论