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对象并返回。
mybatis与hibernate不同
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
Mybatis架构
1.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核心包、依赖包、数据库驱动包。
配置文件:
log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
SqlMapConfig.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <!-- 配置别名扫描器 --> 7 <typeAliases> 8 <package name="cn.mybatis.pojo"/> 9 <package name="cn.mybatis.vo"/> 10 </typeAliases> 11 <environments default="development"> 12 <environment id="development"> 13 <!-- 使用jdbc管理事务 --> 14 <transactionManager type="JDBC"/> 15 <!-- 数据库连接池 --> 16 <dataSource type="POOLED"> 17 <property name="driver" value="com.mysql.jdbc.Driver" /> 18 <property name="url" value="jdbc:mysql://localhost:3306/lucene"/> 19 <property name="username" value="root"/> 20 <property name="password" value="root"/> 21 </dataSource> 22 </environment> 23 </environments> 24 <!-- 配置扫描xml文件 --> 25 <mappers> 26 <package name="cn.mybatis.mapper"/> 27 </mappers> 28 </configuration>
创建pojo
1 package cn.mybatis.pojo; 2 3 4 import java.io.Serializable; 5 import java.util.Date; 6 import java.util.List; 7 8 public class User implements Serializable { 9 /** 10 * 11 */ 12 private static final long serialVersionUID = 1L; 13 private Integer id; 14 private String username;// 用户姓名 15 private String sex;// 性别 16 private Date birthday;// 生日 17 private String address;// 地址 18 private List<Orders> orderList; 19 20 public List<Orders> getOrderList() { 21 return orderList; 22 } 23 public void setOrderList(List<Orders> orderList) { 24 this.orderList = orderList; 25 } 26 public Integer getId() { 27 return id; 28 } 29 public void setId(Integer id) { 30 this.id = id; 31 } 32 public String getUsername() { 33 return username; 34 } 35 public void setUsername(String username) { 36 this.username = username; 37 } 38 public String getSex() { 39 return sex; 40 } 41 public void setSex(String sex) { 42 this.sex = sex; 43 } 44 public Date getBirthday() { 45 return birthday; 46 } 47 public void setBirthday(Date birthday) { 48 this.birthday = birthday; 49 } 50 public String getAddress() { 51 return address; 52 } 53 public void setAddress(String address) { 54 this.address = address; 55 } 56 @Override 57 public String toString() { 58 return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address=" 59 + address + ", orderList=" + orderList + "]"; 60 } 61 }
1 package cn.mybatis.pojo; 2 3 import java.io.Serializable; 4 import java.util.Date; 5 6 public class Orders implements Serializable{ 7 /** 8 * 9 */ 10 private static final long serialVersionUID = 1L; 11 12 private Integer id; 13 14 private Integer userId; 15 16 private String number; 17 18 private Date createtime; 19 20 private String note; 21 22 private User user; 23 24 @Override 25 public String toString() { 26 return "Orders [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime 27 + ", note=" + note + ", user=" + user + "]"; 28 } 29 30 public User getUser() { 31 return user; 32 } 33 34 public void setUser(User user) { 35 this.user = user; 36 } 37 38 public Integer getId() { 39 return id; 40 } 41 42 public void setId(Integer id) { 43 this.id = id; 44 } 45 46 public Integer getUserId() { 47 return userId; 48 } 49 50 public void setUserId(Integer userId) { 51 this.userId = userId; 52 } 53 54 public String getNumber() { 55 return number; 56 } 57 58 public void setNumber(String number) { 59 this.number = number == null ? null : number.trim(); 60 } 61 62 public Date getCreatetime() { 63 return createtime; 64 } 65 66 public void setCreatetime(Date createtime) { 67 this.createtime = createtime; 68 } 69 70 public String getNote() { 71 return note; 72 } 73 74 public void setNote(String note) { 75 this.note = note == null ? null : note.trim(); 76 } 77 78 79 80 }
sql映射文件
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="cn.mybatis.mapper.UserMapper" > 6 <sql id="selectUser"> 7 select * from user 8 </sql> 9 <select id="getAllUser" resultType="cn.mybatis.pojo.User"> 10 <include refid="selectUser"/>; 11 </select> 12 <select id="multiConditionQuery" resultType="User" parameterType="User"> 13 select * from user 14 <where> 15 <if test="id!=null">id=#{id}</if> 16 <if test="username!=null">And username like "%"#{username}"%"</if> 17 <if test="sex!=null">And sex=#{sex}</if> 18 <if test="address!=null">And address=#{address}</if> 19 </where> 20 </select> 21 <select id="count" resultType="Integer"> 22 select count(*) from user; 23 </select> 24 <select id="queryUserByIds" resultType="user" parameterType="UserVo"> 25 select * from user 26 where 27 <foreach collection="ids" item="id" open="id in(" separator="," close=")"> 28 #{id} 29 </foreach> 30 ; 31 </select> 32 <insert id="insert" parameterType="User"> 33 <!-- --> 34 <selectKey keyProperty="id" resultType="Integer" order="AFTER"> 35 select LAST_INSERT_ID(); 36 </selectKey> 37 insert into user 38 (username,birthday,sex,address) 39 value(#{username},#{birthday},#{sex},#{address}); 40 </insert> 41 <delete id="deleteById" parameterType="Integer"> 42 delete from user 43 where id = #{v} 44 </delete> 45 <!-- 多对一 --> 46 <resultMap type="User" id="selectAllUserAndOrderMap"> 47 <id column="id" property="id"/> 48 <id column="username" property="username"/> 49 <id column="address" property="address"/> 50 <collection property="orderList" javaType="List" ofType="Orders"> 51 <result column="number" property="number"/> 52 <result column="createtime" property="createtime"/> 53 </collection> 54 </resultMap> 55 <select id="selectAllUserAndOrder" resultMap="selectAllUserAndOrderMap"> 56 select u.id,u.username,u.address,o.number,o.createtime 57 from user u left join orders o 58 on u.id = o.user_id; 59 </select> 60 61 </mapper>
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="cn.mybatis.mapper.OrderMapper" > 6 <resultMap type="Orders" id="selectAllOrderAndUserMap"> 7 <id column="id" property="id"/> 8 <result column="user_id" property="userId"/> 9 <result column="number" property="number"/> 10 <result column="createtime" property="createtime"/> 11 <result column="note" property="note"/> 12 <association property="user" javaType="User"> 13 <id column="user_id" property="id"/> 14 <result column="username" property="username"/> 15 <result column="address" property="address"/> 16 </association> 17 </resultMap> 18 <select id="selectAllOrderAndUser" resultMap="selectAllOrderAndUserMap"> 19 select o.id,o.user_id,o.number,o.createtime,o.note,u.username,u.address 20 from orders o left join user u 21 on o.user_id = u.id; 22 </select> 23 </mapper>
Mapper接口
public interface UserMapper { List<User> getAllUser(); Integer insert(User user); Integer deleteById(int i); List<User> multiConditionQuery(User user); Integer count(); List<User> queryUserByIds(int[] its); List<User> queryUserByIds(UserVo userVo); List<User> selectAllUserAndOrder(); }
public interface OrderMapper { List<Orders> selectAllOrderAndUser(); }
测试代码
1 package cn.mybatis.demo; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Date; 6 import java.util.List; 7 8 import org.apache.ibatis.io.Resources; 9 import org.apache.ibatis.session.SqlSession; 10 import org.apache.ibatis.session.SqlSessionFactory; 11 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 12 import org.junit.Before; 13 import org.junit.Test; 14 15 import cn.mybatis.mapper.OrderMapper; 16 import cn.mybatis.mapper.UserMapper; 17 import cn.mybatis.pojo.Orders; 18 import cn.mybatis.pojo.User; 19 import cn.mybatis.vo.UserVo; 20 21 public class MyBatisDemo { 22 private SqlSessionFactory sqlSessionFactory; 23 @Before 24 public void getSqlSessionFactory() throws IOException { 25 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); 26 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); 27 } 28 29 @Test 30 public void insertUser() { 31 SqlSession session = sqlSessionFactory.openSession(); 32 UserMapper userMapper = session.getMapper(UserMapper.class); 33 User user = new User(); 34 user.setUsername("zjj"); 35 user.setBirthday(new Date()); 36 user.setSex("1"); 37 System.out.println(userMapper.insert(user)); 38 System.out.println(user.getId()); 39 session.commit(); 40 } 41 42 @Test 43 public void deleteByid() { 44 SqlSession session = sqlSessionFactory.openSession(); 45 UserMapper userMapper = session.getMapper(UserMapper.class); 46 System.out.println(userMapper.deleteById(27)); 47 session.commit(); 48 } 49 50 @Test 51 public void selectAllUser() throws IOException { 52 SqlSession session = sqlSessionFactory.openSession(); 53 UserMapper userMapper = session.getMapper(UserMapper.class); 54 List<User> list = userMapper.getAllUser(); 55 for (User user : list) { 56 System.out.println(user); 57 } 58 session.commit(); 59 } 60 61 @Test 62 public void multiConditionQuery() { 63 SqlSession session = sqlSessionFactory.openSession(); 64 UserMapper userMapper = session.getMapper(UserMapper.class); 65 User user = new User(); 66 user.setAddress("河南郑州"); 67 user.setSex("1"); 68 user.setUsername("明"); 69 List<User> userList = userMapper.multiConditionQuery(user); 70 for (User user2 : userList) { 71 System.out.println(user2); 72 } 73 } 74 75 @Test 76 public void Count() { 77 SqlSession session = sqlSessionFactory.openSession(); 78 UserMapper userMapper = session.getMapper(UserMapper.class); 79 Integer countNum = userMapper.count(); 80 System.out.println(countNum); 81 82 } 83 @Test 84 public void queryUserByIds() { 85 SqlSession session = sqlSessionFactory.openSession(); 86 UserMapper userMapper = session.getMapper(UserMapper.class); 87 88 /*List<User> userList = userMapper.queryUserByIds(its);*/ 89 UserVo userVo = new UserVo(); 90 userVo.getIds().add(24); 91 userVo.getIds().add(25); 92 userVo.getIds().add(26); 93 List<User> userList = userMapper.queryUserByIds(userVo); 94 for (User user2 : userList) { 95 System.out.println(user2); 96 } 97 } 98 @Test 99 //一对一查询 100 public void one2One() { 101 SqlSession session = sqlSessionFactory.openSession(); 102 OrderMapper userMapper = session.getMapper(OrderMapper.class); 103 List<Orders> orderList = userMapper.selectAllOrderAndUser(); 104 for (Orders order : orderList) { 105 System.out.println(order); 106 } 107 } 108 @Test 109 //一对多查询 110 public void one2many() { 111 SqlSession session = sqlSessionFactory.openSession(); 112 UserMapper userMapper = session.getMapper(UserMapper.class); 113 List<User> userList =userMapper.selectAllUserAndOrder(); 114 for (User user2 : userList) { 115 System.out.println(user2); 116 } 117 } 118 119 }
注意点:
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。
#{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换,
${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
<insert id="insert" parameterType="User"> <!-- selectKey 标签实现主键返回 --> <!-- keyColumn:主键对应的表中的哪一列 --> <!-- keyProperty:主键对应的pojo中的哪一个属性 --> <!-- order:设置在执行insert语句前执行查询id的sql,孩纸在执行insert语句之后执行查询id的sql --> <selectKey keyProperty="id" resultType="Integer" order="AFTER"> select LAST_INSERT_ID(); </selectKey> insert into user (username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address}); </insert>
如果是UUID时order="before"
SqlMapConfig.xml中配置的内容:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
自定义别名
<typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="cn.itcast.mybatis.pojo.User" /> <!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) --> <package name="cn.itcast.mybatis.pojo" /> <package name="其它包" /> </typeAliases>
mappers(映射器)
<mapper resource=" " />
使用相对于类路径的资源(现在的使用方式)
如:<mapper resource="sqlmap/User.xml" />
<mapper class=" " />
使用mapper接口类路径
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name=""/>
注册指定包下的所有mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
Mybatis整合spring
整合思想:
1、SqlSessionFactory对象应该放到spring容器中作为单例存在。
2、传统dao的开发方式中,应该从spring容器中获得sqlsession对象。
3、Mapper代理形式中,应该从spring容器中直接获得mapper的代理对象。
4、数据库的连接以及数据库连接池事务管理都交给spring容器来完成。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd 9 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> 10 11 <!-- 加载配置文件 --> 12 <context:property-placeholder location="classpath:db.properties" /> 13 14 <!-- 数据库连接池 --> 15 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 16 destroy-method="close"> 17 <property name="driverClassName" value="${jdbc.driver}" /> 18 <property name="url" value="${jdbc.url}" /> 19 <property name="username" value="${jdbc.username}" /> 20 <property name="password" value="${jdbc.password}" /> 21 <property name="maxActive" value="10" /> 22 <property name="maxIdle" value="5" /> 23 </bean> 24 25 <!-- 配置SqlSessionFactory --> 26 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 27 <!-- 配置mybatis核心配置文件 --> 28 <property name="configLocation" value="classpath:SqlMapConfig.xml" /> 29 <!-- 配置数据源 --> 30 <property name="dataSource" ref="dataSource" /> 31 </bean> 32 </beans>
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
配置mapper代理
第一种
<!-- Mapper代理的方式开发方式一,配置Mapper代理对象 --> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <!-- 配置Mapper接口 --> <property name="mapperInterface" value="cn.itcast.mybatis.mapper.UserMapper" /> <!-- 配置sqlSessionFactory --> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
第二种
<!-- Mapper代理的方式开发方式二,扫描包方式配置代理 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 配置Mapper接口 --> <property name="basePackage" value="cn.itcast.mybatis.mapper" /> </bean>
Mybatis逆向工程
github:https://github.com/wcyong/mybatisGeneratorCustom
修改配置文件
在generatorConfig.xml中配置Mapper生成的详细信息
- 修改要生成的数据库表
- pojo文件所在包路径
- Mapper所在的包路径
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE generatorConfiguration 3 PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" 4 "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> 5 6 <generatorConfiguration> 7 <context id="testTables" targetRuntime="MyBatis3"> 8 <commentGenerator> 9 <!-- 是否去除自动生成的注释 true:是 : false:否 --> 10 <property name="suppressAllComments" value="true" /> 11 </commentGenerator> 12 <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> 13 <jdbcConnection driverClass="com.mysql.jdbc.Driver" 14 connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="root"> 15 </jdbcConnection> 16 <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 17 userId="yycg" password="yycg"> </jdbcConnection> --> 18 19 <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 20 和 NUMERIC 类型解析为java.math.BigDecimal --> 21 <javaTypeResolver> 22 <property name="forceBigDecimals" value="false" /> 23 </javaTypeResolver> 24 25 <!-- targetProject:生成PO类的位置 --> 26 <javaModelGenerator targetPackage="cn.itcast.ssm.po" 27 targetProject=".src"> 28 <!-- enableSubPackages:是否让schema作为包的后缀 --> 29 <property name="enableSubPackages" value="false" /> 30 <!-- 从数据库返回的值被清理前后的空格 --> 31 <property name="trimStrings" value="true" /> 32 </javaModelGenerator> 33 <!-- targetProject:mapper映射文件生成的位置 --> 34 <sqlMapGenerator targetPackage="cn.itcast.ssm.mapper" 35 targetProject=".src"> 36 <!-- enableSubPackages:是否让schema作为包的后缀 --> 37 <property name="enableSubPackages" value="false" /> 38 </sqlMapGenerator> 39 <!-- targetPackage:mapper接口生成的位置 --> 40 <javaClientGenerator type="XMLMAPPER" 41 targetPackage="cn.itcast.ssm.mapper" targetProject=".src"> 42 <!-- enableSubPackages:是否让schema作为包的后缀 --> 43 <property name="enableSubPackages" value="false" /> 44 </javaClientGenerator> 45 <!-- 指定数据库表 --> 46 <table schema="" tableName="user"></table> 47 <table schema="" tableName="order"></table> 48 </context> 49 </generatorConfiguration>
注意:
1. 逆向工程生成的代码只能做单表查询
2. 不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了。
3. 一张表会生成4个文件