MyBatis开发实践
一、mybatis的两种开发方式
- 使用原生接口
- Mapper代理实现自定义接口
二、使用原生接口开发
-
在数据库Test下新建表
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_account -- ---------------------------- DROP TABLE IF EXISTS `t_account`; CREATE TABLE `t_account` ( `id` int(0) NOT NULL AUTO_INCREMENT, `username` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `age` int(0) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
-
在Idea中新建普通maven工程,pom.xml添加如下依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="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"> <modelVersion>4.0.0</modelVersion> <groupId>org.simon</groupId> <artifactId>app</artifactId> <version>1.0-SNAPSHOT</version> <properties> <simon.version>1.0.0</simon.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <mybatis.version>3.5.7</mybatis.version> <mysql-jdbc.verison>8.0.26</mysql-jdbc.verison> <lombok.version>1.18.20</lombok.version> </properties> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-jdbc.verison}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies> <!--通常情况下,mybatis只能读取resources下的xml文件,为了能读取java下的xml文件,做如下配置--> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> </project>
-
新建实体类Account
package com.simon.app.entity; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class Account { private int id; private String username; private String password; private int age; }
@AllArgsConstructor注解自动生成以所有属性作为参数的构造函数
-
在resources目录下建立mybatis配置文件config.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> <environments default="development"> <environment id="development"> <!--配置jdbc事务管理--> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/simon/app/mapper/AccountMapper.xml"></mapper> </mappers> </configuration>
-
建立mapper映射文件AccountMapper.xml
<?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.simon.app.mapper.AccountMapper"> <insert id="save" parameterType="com.simon.app.entity.Account"> insert into t_account(username,password,age) values (#{username},#{password},#{age}) </insert> </mapper>
- namespace的值为映射文件的全路径名去掉xml扩展名
- insert中的id就是对外提供调用的方法名
-
测试代码
package com.simon.app.test; import com.simon.app.entity.Account; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class Test { public static void main(String[] args) { InputStream resource = Test.class.getClassLoader().getResourceAsStream("config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(resource); SqlSession sqlSession = sessionFactory.openSession(); String statement="com.simon.app.mapper.AccountMapper.save"; Account account=new Account(1,"simon","111111",20); sqlSession.insert(statement,account); sqlSession.commit(); sqlSession.close(); } }
三、通过mapper代理实现自定义接口
只需要做两件事
-
自定义接口,定义相关业务方法。
-
编写与方法相对应的Mapper.xml
-
自定义接口
package com.simon.app.repository; import com.simon.app.entity.Account; import java.util.List; public interface AccountRepository { public int save(Account obj); public int update(Account obj); public int delete(int id); public List<Account> findAll(); public Account findById(int id); }
-
不需要提供实现类,直接提供mapper.xml即可,定义接口方法对应的SQL语句,statement 标签可根据SQL执行的业务选择insert、update、delete、select。mybatis会根据规则自动创建接口对应的实现类。
规则:
-
Mapper.xml中的namespace为接口的全类名
-
Mapper.xml中statement中的id为接口中对应的方法名
-
Mapper.xml中的statement的parameterType和接口中对应的方法的参数类型一致
-
Mapper.xml中的statement的resultType和接口中对应的方法的返回值类型一致
<?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.simon.app.repository.AccountRepository"> <insert id="save" parameterType="com.simon.app.entity.Account"> insert into t_account(username,password,age) values (#{username},#{password},#{age}) </insert> <update id="update" parameterType="com.simon.app.entity.Account"> update t_account set username=#{username},password=#{password},age=#{age} where id=#{id} </update> <delete id="delete" parameterType="java.lang.Long"> delete from t_account where id=#{id} </delete> <select id="findAll" resultType="com.simon.app.entity.Account"> select * from t_account </select> <select id="findById" parameterType="int" resultType="com.simon.app.entity.Account"> select * from t_account where id=#{id} </select> </mapper>
-
-
在config.xml中注册AccountRepository.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> <environments default="development"> <environment id="development"> <!--配置jdbc事务管理--> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/simon/app/mapper/AccountMapper.xml"></mapper> <mapper resource="com/simon/app/repository/AccountRepository.xml"></mapper> </mappers> </configuration>
-
测试
package com.simon.app.test; import com.simon.app.entity.Account; import com.simon.app.repository.AccountRepository; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; import java.util.List; public class Test2 { public static void main(String[] args) { InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resource); SqlSession sqlSession = factory.openSession(); //获取接口的代理对象 AccountRepository mapper = sqlSession.getMapper(AccountRepository.class); // Account obj=new Account(2,"linda","111111",46); // mapper.save(obj); // sqlSession.commit(); // sqlSession.close(); // List<Account> all = mapper.findAll(); // for(Account obj:all){ // System.out.println(obj); // } Account account = mapper.findById(3); System.out.println(account); account.setPassword("123456"); account.setUsername("simon"); account.setAge(56); mapper.update(account); sqlSession.commit(); sqlSession.close(); } }
-
四、多条件查询
-
-
接口中定义一个方法
public Account findByNameAndAge(String username,int age);
-
在mapper.xml增加如下statement
<select id="findByNameAndAge" resultType="com.simon.app.entity.Account"> select * from t_account where username =#{param1} and age=#{param2} </select>
由于接口方法中有多个参数传入,而select中不能设置多个parameterType,索性就不写了,取而代之的是在SQL语句中用占位符param1和param2代替,从而达到多条件查询的目的,param名称也可用arg替换,如:arg1, arg0, param1, param2
-
五、一对多和多对一级联查询
5.1、建表脚本
-
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for classes -- ---------------------------- DROP TABLE IF EXISTS `classes`; CREATE TABLE `classes` ( `id` int(0) NOT NULL AUTO_INCREMENT, `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of classes -- ---------------------------- INSERT INTO `classes` VALUES (1, '一班'); INSERT INTO `classes` VALUES (2, '二班'); -- ---------------------------- -- Table structure for student -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(0) NOT NULL AUTO_INCREMENT, `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `cid` int(0) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, INDEX `class_student`(`cid`) USING BTREE, CONSTRAINT `class_student` FOREIGN KEY (`cid`) REFERENCES `classes` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of student -- ---------------------------- INSERT INTO `student` VALUES (1, '小黄', 1); INSERT INTO `student` VALUES (2, '校长', 1); INSERT INTO `student` VALUES (3, '厨子', 2); INSERT INTO `student` VALUES (4, '胖子', 2); INSERT INTO `student` VALUES (5, '诸葛亮', 2); SET FOREIGN_KEY_CHECKS = 1;
5.2 、从多方级联查询
一个班级对应多个学生,如果查询学生并级联查询出对应的班级就是从多方进行查询。
- pojo定义
package com.simon.app.entity;
import lombok.Data;
import java.util.List;
@Data
public class Classes {
private long id;
private String name;
private List<Student> students;
}
package com.simon.app.entity;
import lombok.Data;
@Data
public class Student {
private long id;
private String name;
private Classes classes;
}
- 定义pojo时不能使用@AllArgsConstructor注解
- 此时学生pojo里定义一个班级属性classes
-
定义SQL
select s.id,s.name,c.id as cid,c.`name` as cname from student s,classes c where s.id=#{id} and s.cid=c.id
该语句通过学生id号查询,并关联查询对应的班级。
-
定义resultMap
默认情况下,mybatis的select元素中resultType属性对应的是一个独立的javabean,sql语句中的字段名称和javabean中的属性通过名称一一对应,但是关联查询时,无法从sql查询语句中的字段名称对应到javabean中的对象属性上,为此需要重新定义resultType,而这个resultType就是resultMap,通过上图可知,id和name和Student类的id和name属性一一对应,但是cid和cname如何对应到Student类的classes属性上呢,此时就需要使用association标签了,代码如下:
<resultMap id="studentMap" type="com.simon.app.entity.Student"> <id column="id" property="id"></id> <result column="name" property="name"></result> <association property="classes" javaType="com.simon.app.entity.Classes"> <id column="cid" property="id"></id> <result column="cname" property="name"></result> </association> </resultMap>
-
将select标签的resultType更换为resultMap
<select id="findById" parameterType="long" resultMap="studentMap"> select s.id,s.name,c.id as cid,c.`name` as cname from student s,classes c where s.id=#{id} and s.id=c.id </select>
完整的AccountRepository.xml文件如下:
<?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.simon.app.repository.StudentRepository"> <resultMap id="studentMap" type="com.simon.app.entity.Student"> <id column="id" property="id"></id> <result column="name" property="name"></result> <association property="classes" javaType="com.simon.app.entity.Classes"> <id column="cid" property="id"></id> <result column="cname" property="name"></result> </association> </resultMap> <select id="findById" parameterType="long" resultMap="studentMap"> select s.id,s.name,c.id as cid,c.`name` as cname from student s,classes c where s.id=#{id} and s.id=c.id </select> </mapper>
记住将xml加入到config.xml中
-
定义接口StudentRepository.java
package com.simon.app.repository; import com.simon.app.entity.Student; public interface StudentRepository { public Student findById(long id); }
-
定义测试类
package com.simon.app.test; import com.simon.app.entity.Student; import com.simon.app.repository.StudentRepository; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class Test4 { public static void main(String[] args) { InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resource); SqlSession sqlSession = factory.openSession(); //获取接口的代理对象 StudentRepository mapper = sqlSession.getMapper(StudentRepository.class); Student student = mapper.findById(1L); System.out.println(student); sqlSession.close(); } }
-
运行结果
5.3、从一方级联查询
Student和Classes两个javabean不变。
-
定义测试SQL
select s.id,s.name,c.id as cid,c.`name` as cname from student s,classes c where c.id=1 and s.cid=c.id
结果如下:
可以看到,通过班级id可以查询到对应多条学生数据
-
定义接口ClassesRepository.java
package com.simon.app.repository; import com.simon.app.entity.Classes; public interface ClassesRepository { public Classes findById(long id); }
-
定义ClassesRepository.xml
<?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.simon.app.repository.ClassesRepository"> <resultMap id="classesMap" type="com.simon.app.entity.Classes"> <id column="cid" property="id"></id> <result column="cname" property="name"></result> <collection property="students" ofType="com.simon.app.entity.Student"> <id column="id" property="id"></id> <result column="name" property="name"></result> </collection> </resultMap> <select id="findById" parameterType="long" resultMap="classesMap"> select s.id,s.name,c.id as cid,c.`name` as cname from student s,classes c where c.id=#{id} and s.cid=c.id </select> </mapper>
可以看到集合用collection替换,javaType换成ofType,ofType里面的值实际上是泛型对应的类型,即Student类
sql字段和标签属性的对应关系如下: -
定义测试类
package com.simon.app.test; import com.simon.app.entity.Classes; import com.simon.app.repository.ClassesRepository; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class Test5 { public static void main(String[] args) { InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resource); SqlSession sqlSession = factory.openSession(); //获取接口的代理对象 ClassesRepository mapper = sqlSession.getMapper(ClassesRepository.class); Classes classes = mapper.findById(1L); System.out.println(classes); sqlSession.close(); } }
-
结果