mybatis基础
一、ORM映射
- ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。
- 即为在数据库获取数据后,转化为实体类的过程,使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率
- orm框架代替了jdbc技术
二、MyBatis简介
MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。
三、MyBatis条件
-
引入依赖
<dependencies> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!-- mysql驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> </dependencies>
-
创建SqlMapConfig.xml文件,存入resources文件夹之中
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置环境 --> <environments default="mysql"> <environment id="mysql"> <!-- 事务类型 --> <transactionManagertype="JDBC"></transactionManager> <!-- 数据源 --> <dataSource type="POOLED"> <property name="driver"value="com.mysql.jdbc.Driver"/> <property name="url"value="jdbc:mysql:///mybatis"/> <property name="username"value="root"/> <property name="password"value="root"/> </dataSource> </environment> </environments> </configuration>
用法一:
-
手动创建实体类
-
在src- >main->mapper文件下创建相应的pojomapper.xml文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper3.0//EN" "http://mybatis.org/dtd/mybatis-3- mapper.dtd"> <mappernamespace="com.zbzzs.mapper.UserMapper"> <select id="findAll"resultType="com.zbzzs.pojo.User"> select * from user </select> </mapper>
-
将mapper文件夹注册到SqlMapConfig.xml文件中
<!-- 注册映射文件 --> <mappers> <mapper resource="com/itbaizhan/mapper/UserMapper.xml"> </mapper> </mappers>
注意:
- 映射文件要和接口名称相同
- 映射文件要和接口的目录结构相同
- 映射文件中namespace属性要写接口的全名
四、mybatis使用流程
1.引入常用依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- jsp -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
<version>2.2</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
2.生成SqlMapperConfig.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>
<!-- 配置环境 -->
<properties resource="jdbc.properties"></properties>
<environments default="mysql">
<environment id="mysql">
<!-- 事务类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源 -->
<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>
<mappers>
<package name="mapper"/>
</mappers>
</configuration>
3.映射文件
<?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="com.zbzzs.mapper2.ClassesMapper2">
<!-- 自定义映射关系 -->
<resultMap id="MyClassesMapper" type="com.zbzzs.pojo.Classes">
<id property="cid" column="cid"></id>
<result property="className" column="className"></result>
<!-- select:从表查询调用的方法 column:调用方法时传入的参数字段 -->
<collection property="studentList"
ofType="com.zbzzs.pojo.Student"
select="com.zbzzs.mapper2.StudentMapper2.findByClassId"
column="cid"
fetchType="lazy">
</collection>
</resultMap>
<select id="findAll" resultMap="MyClassesMapper">
select * from classes
</select>
<select id="findByCid" resultType="com.zbzzs.pojo.Classes" parameterType="int">
select * from classes where cid = ${cid}
</select>
</mapper>
4.主键回填(选)
<selectKey keyProperty="id"
keyColumn="id" resultType="int"
order="AFTER">
SELECT LAST_INSERT_ID();
</selectKey>
@SelectKey(keyColumn = "id", keyProperty = "id", resultType = int.class,
before = false, statement = "SELECT LAST_INSERT_ID()")
@Insert("insert into user(username,sex,address) values(#{username},#{sex},#{address})")
void add(User user);
5.注解式开发
@Select("select * from classes where cid = #{cid}")
Classes findByCid(int cid);
// 查询所有班级
@Select("select * from classes")
@Results(id = "classMapper", value = {
@Result(id = true, property = "cid", column = "cid"),
@Result(property = "className", column = "className"),
@Result(property = "studentList", column = "cid",
many = @Many(select = "com.zbzzs.mapper.StudentMapper.findByClassId",
fetchType = FetchType.LAZY))
})
List<Classes> findAll();
public interface UserMapper {
@Results(id = "userMapper",value = {
@Result(id = true,property = "id",column = "id"),
@Result(property = "username",column = "username1"),
@Result(property = "sex",column = "sex1"),
@Result(property = "address",column = "address1")
})
@Select("select * from user")
List<User> findAll();
@ResultMap("userMapper")
@Select("select * from user where id = #{id}")
User findById(int id);
@SelectKey(keyColumn = "id", keyProperty = "id", resultType = int.class,
before = false, statement = "SELECT LAST_INSERT_ID()")
@Insert("insert into user(username,sex,address) values(#{username},#{sex},#{address})")
void add(User user);
@Update("update user set username = #{username},sex=#{sex},address=#{address} where id = #{id}")
void update(User user);
@Delete("delete from user where id = #{id}")
void delete(int id);
@Select("select * from user where username like #{username}")
List<User> findByUsernameLike(String username);
@Select("select * from student")
// 自定义映射关系
@Results(id = "studentMapper",value = {
@Result(id = true,property = "sid",column = "sid"),
@Result(property = "name",column = "name"),
@Result(property = "age",column = "age"),
@Result(property = "sex",column = "sex"),
/**
* property:属性名
* column:调用从表方法时传入的参数列
* one:表示该属性是一个对象
* select:调用的从表方法
* fetchType:加载方式
*/
@Result(property = "classes",column = "classId",
one = @One(select = "com.zbzzs.mapper.ClassesMapper.findByCid",
fetchType = FetchType.EAGER))
})
List<Student> findAll();
// 根据班级ID查询学生
@Select("select * from student where classId = #{classId}")
List<Student> findByClassId(Integer classId);
6.动态sql
<select id="findByCondition" parameterType="com.zbzzs.pojo.User" resultType="com.zbzzs.pojo.User">
select * from user
<where>
<if test="username != null and username.length() != 0">
username like #{username}
</if>
<if test="sex != null and sex.length() != 0">
and sex = #{sex}
</if>
<if test="address != null and address.length() != 0">
and address = #{address}
</if>
</where>
</select>
<update id="updateUser" parameterType="com.zbzzs.pojo.User">
update user
<set>
<if test="username != null and username.length != 0">
username = #{username},
</if>
<if test="sex != null and sex.length != 0">
sex = #{sex},
</if>
<if test="address != null and address.length != 0">
address = #{address}
</if>
</set>
<where>
id = #{id}
</where>
</update>
<select id="findByUsername" resultType="com.zbzzs.pojo.User" parameterType="string">
select * from user
<where>
<choose>
<when test="username.length() < 5">
<bind name="likename" value="'%'+username+'%'"/>
username like #{likename}
</when>
<when test="username.length() < 10">
username = #{username}
</when>
<otherwise>
id = 1
</otherwise>
</choose>
</where>
</select>
<delete id="deleteBatch" parameterType="int">
delete from user
<where>
<foreach open="id in(" close=")" separator="," collection="array" item="id" >
#{id}
</foreach>
</where>
</delete>
<insert id="insertBatch" parameterType="com.zbzzs.pojo.User">
insert into user(username,sex,address) values
<foreach collection="list" item="user" separator=",">
(#{user.username},#{user.sex},#{user.address})
</foreach>
</insert>
<select id="findUser" parameterType="map"resultType="com.zbzzs.pojo.User">
select * from user
<where>
<foreach collection="queryMap" separator="and" index="key" item="value">
${key} = #{value}
</foreach>
</where>
</select>
- if中的条件不能使用&&/||,而应该使用and/or
- if中的条件可以直接通过属性名获取参数POJO的属性值,并且该值可以调用方法。
- where后为什么要加1=1?任意条件都可能拼接到Sql中。如果有多个条件,从第二个条件开始前都需要加And关键字。加上1=1这个永久成立的条件,就不需要考虑后面的条件哪个是第一个条件,后面的条件前都加And关键字即可。
- 可以代替sql中的where 1=1 和第一个and,更符合程序员的开发习惯
- 标签用在update语句中。借助 ,可以只对有具体值的字段进行更新。 会自动添加set关键字,并去掉最后一个if语句中多余的逗号
7.缓存(选)
- MyBatis一级缓存也叫本地缓存(一级默认开启)。
- SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存。
- MyBatis的一级缓存是默认开启的,不需要任何的配置。
清空MyBatis一级缓存
- SqlSession 调用 close() :操作后SqlSession对象不可用,该对象的缓存数据也不可用。
- SqlSession 调用 clearCache() / commit() :操作会清空一级缓存数据。
- SqlSession 调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确。
开启二级缓存
-
对实体类实现可序列化接口( Serializable)
-
在MyBatis配置文件添加如下设置:(默认为true,可省略)
<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
在映射文件添加 标签,该映射文件下的所有方法都支持二级缓存。
<cache size="2048"/>
-
注意:一级缓存对应一个SqlSession 、 二级缓存对应一个SqlSessionFactory对象
注解式二级缓存
在持久层接口上方加注解
@CacheNamespace(blocking=true)
该接口的所有方法都支持二级缓存。
8.分解式查询
//要有相应的接口方法
public interface ClassesMapper {
// 查询所有班级
List<Classes> findAll();
}
public interface StudentMapper {
// 根据班级Id查询学生
List<Student> findByClassId(int classId);
}
<select id="findByClassId"resultType="com.zbzzs.pojo.Student" parameterType="int">
select * from student where classId = ${classId}
</select>
<!-- 自定义映射关系 -->
<resultMap id="MyClassesMapper" type="com.zbzzs.pojo.Classes">
<id property="cid" column="cid"></id>
<result property="className" column="className"></result>
<!-- select:从表查询调用的方法 column:调用方法时传入的参数字段 -->
<collection property="studentList" ofType="com.zbzzs.pojo.Student"
select="com.zbzzs.mapper2.StudentMapper2.findByClassId" column="cid">
</collection>
</resultMap>
<select id="findAll" resultMap="MyClassesMapper">
select * from classes
</select>
9.延迟加载(分解式查询)
-
在 、 中添加fetchType属性设置加载方式。lazy:延迟加载;eager:立即加载。
-
由于打印对象时会调用对象的 toString 方法, toString 方法默认会触发延迟加载的查询,所以我们无法测试出延迟加载的效果。我们在配置文件设置lazyLoadTriggerMethods属性,该属性指定对象的什么方法触发延迟加载,设置为空字符串即可
<settings> <setting name="lazyLoadTriggerMethods" value=""/> </settings>
10.注解开发
-
创建maven工程,引入依赖
-
创建mybatis核心配置文件SqlMapConfig.xml
-
将log4j.properties文件放入resources中,让控制台打印SQL语句
-
创建实体类
-
创建持久层接口,并在接口方法上定义Sql语句
-
在核心配置文件注册持久层接口,由于没有映射文件,所以只能采用注册接口或注册包的方法
<mappers> <package name="com.zbzzs.mapper"/> </mappers>
@SelectKey(keyColumn = "id", keyProperty ="id", resultType = int.class,before = false,statement = "SELECT LAST_INSERT_ID()") @Insert("insert into user(username,sex,address) values(#{username},#{sex},#{address})") void add(User user); @Update("update user set username = #{username},sex=#{sex},address=#{address} where id = #{id}") void update(User user); @Delete("delete from user where id = #{id}") void delete(int id); @Select("select * from user where username like #{username}") List<User> findByUsernameLike(String username);
11.分页插件
-
分页插件
<!-- PageHelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.0</version> </dependency>
-
Mybatis配置文件中配置PageHelper插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 设置数据库类型--> <property name="helperDialect" value="mysql"/> </plugin> </plugins>
-
使用PageHelper插件
@Test public void testFindPage() { // (1)查询前设置分页参数,参数一:页数,从1开始。参数二:每页条数 PageHelper.startPage(1, 3); // (2)正常查询 List<User> all = userMapper.findAll(); // (3)创建页面对象,创建时将查询结果传入构造方法 PageInfo pageInfo = new PageInfo(all); // (4)打印页面对象的属性 System.out.println("结果集:"+pageInfo.getList()); System.out.println("总条数:"+pageInfo.getTotal()); System.out.println("总页数"+pageInfo.getPages()); System.out.println("当前页"+pageInfo.getPageNum()); System.out.println("每页条数"+pageInfo.getSize()); }
12. Generator_工具引入
-
自动生成POJO类、持久层接口与映射文件,极大减少了代码的编写量,提高开发效率
-
在pom文件中配置MBG插件
<build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.7</version> <configuration> <!-- MBG配置文件位置 --> <configurationFile> src/main/resources/generatorConfig.xml </configurationFile > <!-- 运行显示详情 --> <verbose>true</verbose> <!-- 允许覆盖文件 --> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>
-
编写MBG配置文件
<?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> <!-- jdbc的jar包位置,插件需要连接数据库 --> <classPathEntry location="D:\Repository\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar"/> <context id="default" targetRuntime="MyBatis3"> <!-- 是否去除自动生成的注释--> <commentGenerator> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--数据库连接参数--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="root"> </jdbcConnection> <!-- 类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- targetProject:JAVA类路径 targetProject:生成的POJO类的包--> <javaModelGenerator targetProject="src/main/java" targetPackage="com.zbzzs.pojo"> <!-- 是否生成子包 --> <property name="enableSubPackages" value="false"/> <!-- 设置是否在getter方法中,对String类型字段调用trim()方法 --> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- targetProject:配置文件路径 targetPackage:生成映射文件的位置 --> <sqlMapGenerator targetProject="src/main/resources" targetPackage="com.zbzzs.mapper"> <!-- 是否生成子包 --> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- targetPackage:JAVA类路径 targetProject:生成的持久层接口包 --> <javaClientGenerator targetProject="src/main/java" targetPackage="com.zbzzs.mapper" type="XMLMAPPER"> <!-- 是否生成子包 --> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- 数据库表,表名不要和其他库中的表名一样 --> <table tableName="product"></table> </context> </generatorConfiguration>
-
运行插件,自动生成POJO,持久层接口,映射文件:
-
在配置文件中注册生成的映射文件
-
使用案例
public class TestMBG { InputStream is = null; SqlSession session = null; ProductMapper productMapper = null; @Before public void before() throws IOException { is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); session = factory.openSession(); productMapper = session.getMapper(ProductMapper.class); } @After public void after() throws IOException { session.close(); is.close(); } // 新增 @Test public void testAdd(){ Product product = new Product("百战Python课", 15000.0); productMapper.insert(product); session.commit(); } // 修改 @Test public void testUpdate(){ Product product = new Product(5,"百战Python课", 25000.0); productMapper.updateByPrimaryKey(product); session.commit(); } // 删除 @Test public void testDelete(){ productMapper.deleteByPrimaryKey(5); session.commit(); } }
// 根据id查询 @Test public void testFindById() { Product product = productMapper.selectByPrimaryKey(1); System.out.println(product); } // 查询所有 @Test public void testFindAll() { // 查询扩展对象,可以构建查询条件 ProductExample productExample = new ProductExample(); List<Product> products = productMapper.selectByExample(productExample); products.forEach(System.out::println); } // 根据商品名查询 @Test public void testFindByName(){ // 查询扩展对象,可以构建查询条件 ProductExample productExample = new ProductExample(); // 构建查询条件 ProductExample.Criteria criteria = productExample.createCriteria(); criteria.andProductnameLike("%尚学堂%"); // 查询 List<Product> products = productMapper.selectByExample(productExample); products.forEach(System.out::println); }