ORM框架
ORM全称为Object Relational Mapping,关系对象映射,就是将数据表中的一行数据与对象对应起来。
ORM框架提供了持久化类与表的映射关系,运行时把对象持久化到数据库中
Mybatis
Mybatis的前身是iBatis,是一个Java持久层框架。iBatis提供包括了SQL Maps 和 Data Access Objects(DAOs)
Mybatis的使用
在数据库中创建student表
USE mybatis; //创建student表 CREATE TABLE student ( id INT PRIMARY KEY auto_increment not null, name VARCHAR(255), age INT, address VARCHAR(255), email VARCHAR(255) ); //插入数据 INSERT INTO mybatis.student (id, name, age, address, email) VALUES (1, '张三', 20, '北京', '[email protected]'); INSERT INTO mybatis.student (id, name, age, address, email) VALUES (2, '李四', 21, '成都', '[email protected]'); INSERT INTO mybatis.student (id, name, age, address, email) VALUES (3, '王五', 45, '上海', '[email protected]'); INSERT INTO mybatis.student (id, name, age, address, email) VALUES (4, '零六', 23, '杭州', '[email protected]'); INSERT INTO mybatis.student (id, name, age, address, email) VALUES (5, '二狗子', 63, '深圳', '[email protected]');
一、创建工程
<!-- Mybatis的包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- JDBC的包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
二、创建配置文件
在resources目录下创建config.xml
Mybatis支持properties文件的引入,这样做的目的就是为了区分配置:不同的文件中描述不同的配置,这样方便管理。
建立jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456
在config.xml文件中引入
注意:mybatis的配置文件的标签必须按照顺序配置,否则会报错
配置顺序:properties, settings, typeAliases, typeHandlers, objectFactory, objectWrapperFactory, reflectorFactory, plugins, environments, databaseIdProvider, mappers
<?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"/>
<typeAliases>
<!--这里是针对整个包中的类取别名, 别名就是类名,不区分大小写-->
<package name="cn.cnmd.pojo"/>
</typeAliases>
<!--environments代表环境,可以包含多个,比如开发环境、测试环境、生产环境-->
<environments default="dev">
<environment id="dev">
<!--事务管理器,一般默认JDBC-->
<transactionManager type="JDBC"/>
<!--数据源,一般默认是POOLED-->
<dataSource type="POOLED">
<!--1.3配置连接池需要的参数-->
<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>
<mapper resource="mapper/studentMapper.xml"/>
</mappers>
</configuration>
在resources/mapper目录下创建studentMapper.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="cn.cnmd.mapper.StudentMapper">
<select id="getStudents" resultType="Student">
select id, name, age, address, email
from mybatis.student;
</select>
</mapper>
三、创建接口/类
在java目录下创建对应的接口cn.cnmd.mapper.StudentMapper
public interface StudentMapper {
List<Student> getStudents();
}
在java目录下创建表对应的实体类cn.cnmd.pojo.Student
@Data
public class Student {
private Integer id;
private String name;
private Integer age;
private String address;
private String email;
}
四、查询
创建一个测试类EasyUseTest
public class EasyUseTest {
public static void main(String[] args) throws IOException {
// 获取配置文件
InputStream in = Resources.getResourceAsStream("config.xml");
// 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 通过SqlSessionFactoryBuilder对象创建SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
// 通过SqlSessionFactory创建SqlSession
SqlSession session = factory.openSession();
// 通过SqlSession创建StudentMapper
StudentMapper mapper = session.getMapper(StudentMapper.class);
// 通过StudentMapper获取学生列表
List<Student> students = mapper.getStudents();
// 遍历学生列表,打印学生信息
students.forEach(System.out::println);
}
}
查询结果
Student(id=1, name=张三, age=20, address=北京, [email protected])
Student(id=2, name=李四, age=21, address=成都, [email protected])
Student(id=3, name=王五, age=45, address=上海, [email protected])
Student(id=4, name=零六, age=23, address=杭州, [email protected])
Student(id=5, name=二狗子, age=63, address=深圳, [email protected])
Mybatis配置优化
一、propertise文件配置
在指定数据源时,为了方便管理,Mybatis支持引入properties配置文件
创建包含了连接信息的jdbc.properties配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456
在config.xml中引入并使用
<!--通过peroperties标签引入配置文件-->
<properties resource="jdbc.properties"/>
<!--..........-->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--1.3配置连接池需要的参数-->
<!--使用 ${参数名} 的方式进行赋值-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
二、为mapper中的类取别名
在studentMapper.xml文件中,定义一个mapper时,如果时select操作,则需要指定resultType="cn.cnmd.pojo.Student"
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.cnmd.mapper.StudentMapper">
<select id="getStudents" resultType="cn.cnmd.pojo.Student">
select id, name, age, address, email
from mybatis.student;
</select>
</mapper>
为了简化配置,可以在config.xml文件中指定
<typeAliases>
<!--使用 typeAlias标签 是针对某一个类,别名为alias的值,resultType必须一致-->
<!--<typeAlias type="cn.cnmd.pojo.Student" alias="stu"/>-->
<!--使用 package标签 是针对整个包中的类取别名,别名就是类名,不区分大小写-->
<package name="cn.cnmd.pojo"/>
</typeAliases>
三、日志配置
有时,我们需要看到sql执行的详细内容,就可以配置日志打印
<settings>
<!-- 打印SQL语句 STDOUT_LOGGING是一个类的别名:
org.apache.ibatis.logging.stdout.StdOutImpl-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Mybatis的增删改查
增删改查都需要在对应的接口中定义对应的增删改查的方法
public interface StudentMapper {
// 添加学生
int addStudent(Student student);
// 删除学生
int deleteStudent(int id);
// 更新学生
int updateStudent(Student student);
//根据id查找某个学生
Student getStudent(int id);
// 获取学生列表
List<Student> getStudents();
}
一、增加(insert)
在stuentMapper.xml文件中定义insert标签,id对应的接口中的方法名,没有返回值类型
没有resultType是因为除了查询,增删改操作的返回结果都是AffectedRows受影响的行数,是整数
参数匹配规则
- 如果方法传递的是简单类型参数,比如String、int、long等以及对应的包装类:使用 #{ arg+参数下标 }或者#{ param+参数位置 } 进行占位
select * from student where name = #{ arg0 }and age = #{ arg1 } select * from student where name = #{ param1 }and age = #{ param2 }
- 如果方法传递的是单个对象参数,比如学生类Student:使用 #{ 类的属性名 } 进行占位
insert into student (name, age, address, email) values(#{name}, #{student.age}, #{address}, #{eamil})
- 如果方法传递的是多个复杂类型参数,比如 学生类Student 和 简单数据类型:简单数据类型使用 #{ arg+参数下标 }或者#{ param+参数位置 },复杂数据类型使用 #{ arg+参数下标.属性名 }或者#{ param+参数位置.属性名 }
select name, age from student where id = #{ arg0 } and sex = #{ arg1.name }
- 如果在接口方法中的参数使用了@Param(“参数名”)注解:在就必须使用 #{ 参数名 }
<insert id="addStudent">
insert into mybatis.student (name, age, address, email)
values (#{name},#{age},#{address},#{email})
</insert>
二、删除(delete)
在stuentMapper.xml文件中定义delete标签,id对应的接口中的方法名,没有返回值类型
根据条件查询的时候,方法传递了参数 int id,所以可以通过#{id} / #{arg0} / #{param1}的方式进行占位匹配
<delete id="deleteStudent">
delete
from mybatis.student
where id = #{param1}
</delete>
三、修改(update)
在stuentMapper.xml文件中定义update标签,id对应的接口中的方法名,没有返回值类型
<update id="updateStudent">
update mybatis.student
set name = #{arg0.name}
where id = #{arg1}
</update>
四、查询(select)
在stuentMapper.xml文件中定义select标签,id对应的接口中的方法名,返回值类型为一条数据行对应的实体类的类型,如上文的student
<select id="getStudent" resultType="student">
select id, name, age, address, email
from mybatis.student
where id = #{arg0}
</select>
主键回传
主键回填的应用场景: 如果一个业务,需要增加一条记录,然后还需要增加的相关的记录,这个相关的记录就需要使用到前一条记录的主键。
需要在studentMapper.xml文件中的插入标签中增加一个insert标签
注意点:
useGeneratedKeys="true"
表示使用自动生成的键parameterType="student"
表示参数类型为studentselectKey
标签定义了一个查询键,用于获取插入操作后的主键值。键属性为id
,结果类型为int
,查询语句为select last_insert_id();
,查询结果将赋值给student
的id
属性。
<insert id="addStudent" useGeneratedKeys="true" parameterType="student">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into mybatis.student (name, age, address, email)
values (#{name}, #{age}, #{address}, #{email})
</insert>
Java代码
Student student = new Student();
student.setName("南宫婉");
student.setAge(24);
student.setAddress("楚国");
student.setEmail("[email protected]");
int i = mapper.addStudent(student);
Integer id = student.getId();
System.out.println("回填的主键是:" + id);//回填的主键是:6
结果映射
在实际情况中,很有可能出现设计的实体类的属性名与数据表的字段不匹配的情况
针对这种情况,Mybatis提供了结果集映射,供用户自己实现数据库中的字段与实体类的属性匹配
创建一个teacher表,并插入数据
CREATE TABLE teacher
(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(255),
t_age INT
);
INSERT INTO teacher (t_name, t_age)
VALUES ('张三', 30),
('李四', 40),
('王五', 50),
('赵六', 60),
('孙七', 70);
创建一个Teacher类,其中的字段与数据表并不匹配
@Data
public class Teacher {
private Integer id;
private String name;
private Integer age;
}
创建teacherMapper接口并定义查询方法
public interface TeacherMapper {
// 获取学生列表
List<Teacher> getTeachers();
}
创建teacherMapper.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="cn.cnmd.mapper.TeacherMapper">
<!--定义了一个resultMap来定义映射关系
property代表实体类的属性
column代表数据表的列名
-->
<resultMap id="resultMap" type="teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
<result property="age" column="t_age"/>
</resultMap>
<select id="getTeachers" resultMap="resultMap"><!--指定映射的map为resultMap-->
select t_id, t_name, t_age
from mybatis.teacher;
</select>
</mapper>
Java代码
TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
List<Teacher> teachers = teacherMapper.getTeachers();
teachers.forEach(System.out::println);
查询结果,可以看到实体类的属性与表列名对应上了
Teacher(id=1, name=张三, age=30)
Teacher(id=2, name=李四, age=40)
Teacher(id=3, name=王五, age=50)
Teacher(id=4, name=赵六, age=60)
Teacher(id=5, name=孙七, age=70)
注意:Mybatis只是提供了结果集映射,并不是一定要使用,直接在查询语句中为表字段名取别名可以不使用结果映射
因为查询的结果返回的是一张虚拟表,表的列名就是别名
select t_id id, t_name name, t_age age from teacher;
联级查询
数据表存在一对一的关系,一对多的关系,多对多的关系
多对多的关系就是多个一对多关系的组合,并借助中间表来实现
这里的联级查询就只由一对一联级查询、一对多联级查询
一对一联级查询
一对一的情况就是一张表只与另一张表关联,比如老师对学生进行一对一辅导对应的一对一关系
准备数据表my_student,my_teacher
CREATE TABLE my_student
(
id INT PRIMARY KEY auto_increment,
name VARCHAR(255),
teacher_id INT,
FOREIGN KEY (teacher_id) REFERENCES my_teacher (id)
);
CREATE TABLE my_teacher
(
id INT PRIMARY KEY auto_increment,
name VARCHAR(255)
);
INSERT INTO my_student (id, name, teacher_id)
VALUES (1, '张三', 1),
(2, '李四', 2);
INSERT INTO my_teacher (id, name)
VALUES (1, '王五'),
(2, '赵六');
两个实体类MyStudent、MyTeacher
@Data
public class MyTeacher {
private Integer id;
private String name;
}
//------------------------------------------------------------
@Data
public class MyStudent {
private Integer id;
private String name;
private Integer teacherId;
}
创建studentTeacherMapper接口
public interface StudentTeacherMapper {
List<MyStudent> getStudents();
}
创建studentTeacherMapper.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="cn.cnmd.mapper.StudentTeacherMapper">
<resultMap id="resultMap" type="MyStudent">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="myTeacher" column="{id = id}" select="getTeacher"/>
</resultMap>
<select id="getStudents" resultMap="resultMap">
select id, name, teacher_id
from mybatis.my_student;
</select>
<select id="getTeacher" resultType="MyTeacher">
select id, name
from mybatis.my_teacher where id = #{id};
</select>
<!-- <resultMap id="resultMap2" type="myStudent"> -->
<!-- <id property="id" column="id"/> -->
<!-- <result property="name" column="name"/> -->
<!-- <association property="teacher" javaType="myTeacher"> -->
<!-- <id property="id" column="teacherId"/> -->
<!-- <result property="name" column="teacherName"/> -->
<!-- </association> -->
<!-- </resultMap> -->
<!-- <select id="getStudents" resultMap="resultMap2"> -->
<!-- select stu.id, stu.name, tea.id as teacherId, tea.name as teacherName -->
<!-- from mybatis.my_student stu -->
<!-- inner join mybatis.my_teacher tea -->
<!-- where tea.id = stu.teacher_id; -->
<!-- </select> -->
</mapper>
Java代码
StudentTeacherMapper mapper = session.getMapper(StudentTeacherMapper.class);
List<MyStudent> students = mapper.getStudents();
students.forEach(System.out::println);
结果
MyStudent(id=1, name=张三, myTeacher=MyTeacher(id=1, name=王五))
MyStudent(id=2, name=李四, myTeacher=MyTeacher(id=2, name=赵六))
一对多联级查询
品牌和产品的关系就是一对多的关系,比如苹果和水果拼盘都属于苹果品牌,香蕉属于香蕉品牌
新建表brand、product
# 一对多的关系
CREATE TABLE product
(
id INT PRIMARY KEY,
name VARCHAR(255),
brand_id INT,
FOREIGN KEY (brand_id) REFERENCES brand (id)
);
CREATE TABLE brand
(
id INT PRIMARY KEY,
name VARCHAR(255)
);
INSERT INTO product (id, name, brand_id)
VALUES (1, '苹果', 1),
(2, '香蕉', 2),
(3, '水果拼盘', 1);
INSERT INTO brand (id, name)
VALUES (1, '苹果品牌'),
(2, '香蕉品牌');
创建实体类Brand、Product
@Data
public class Brand {
private Integer id;
private String name;
private List<Product> products;
}
//------------------------------------------
@Data
public class Product {
private Integer id;
private String name;
private Integer bandId;
}
定义BrandMapper接口
public interface BrandMapper {
List<Brand> getBrands();
}
创建brandMapper.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="cn.cnmd.mapper.BrandMapper">
<!-- 方案一 -->
<resultMap id="getBrandsMap" type="brand">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="products" column="{brandId = id}" select="getProducts">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="brandId" column="brand_id"/>
</collection>
</resultMap>
<select id="getBrands" resultMap="getBrandsMap">
select id, name
from mybatis.brand;
</select>
<select id="getProducts" resultType="product">
select id, name, brand_id
from mybatis.product
where brand_id = #{brandId}
</select>
<!-- 方案二 -->
<!-- <resultMap id="map" type="brand"> -->
<!-- <id property="id" column="id"/> -->
<!-- <result property="name" column="name"/> -->
<!-- <collection property="products" ofType="product"> -->
<!-- <id property="id" column="productId"/> -->
<!-- <result property="name" column="productName"/> -->
<!-- <result property="brandId" column="belongToBrand"/> -->
<!-- </collection> -->
<!-- </resultMap> -->
<!-- <select id="getBrands" resultMap="map"> -->
<!-- select b.id, b.name, p.id productId, p.name productName, p.brand_id belongToBrand -->
<!-- from mybatis.brand b -->
<!-- inner join mybatis.product p -->
<!-- where b.id = p.brand_id -->
<!-- </select> -->
</mapper>
Java代码
BrandMapper mapper = session.getMapper(BrandMapper.class);
List<Brand> brands = mapper.getBrands();
brands.forEach(System.out::println);
查询结果
Brand(id=1, name=苹果品牌, products=[Product(id=1, name=苹果, brandId=1), Product(id=3, name=水果拼盘, brandId=1)])
Brand(id=2, name=香蕉品牌, products=[Product(id=2, name=香蕉, brandId=2)])