什么是数据持久化
数据持久化是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。例如:文件的存储,数据的读取等都是数据持久化的操作。狭义的理解: “持久化”仅仅指把域对象永久保存到数据库中;广义的理解,“持久化”包括增删改查等数据库相关的各种操作。
Mybatis框架介绍
Mybatis是一个开源的半ORM1框架,因为它在查询对象和关联对象集合时需要手动写sql语句。
为什么使用Mybatis?
- Mybatis消除了大量的JDBC冗余代码
- Mybatis有低的学习曲线
- Mybatis能很好地与传统数据库协同工作
- Mybatis可以接受SQL语句
- Mybatis提供了与Spring框架的集成支持
- Mybatis提供了与第三方缓存类库的集成支持
- Mybatis引入了更好的性能
搭建Mybatis环境
- 下载jar包
- 创建Mybatis核心配置文件
mybatis的配置文件一般来说名字是固定的,就叫 mybatis-config.xml ,其中包括数据库连接信息,类型别名,映射文件路径等等;直接创建在src目录下。
案例:
<?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>
<typeAliases>
<typeAlias alias="Student" type="com.test.pojo.Student"
/>
</typeAliases>
<settings>
<setting name="logImpl" value="LOG4J"></setting>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/test/pojo/StudentMapper.xml" />
</mappers>
</configuration>
mybatis核心配置文件的节点元素介绍:
- configuration:配置文件的根节点元素
- properties:通过resources属性从外部指定包括数据源等配置文件,使用${属性名}引用。
- environments:是配置mybatis当前工作的数据库环境的地方
- dataSource 表示的是数据源;至少会包括该连接数据库的四种信息.dataSource的类型type属性可以配置成其内置类型之一,如UNPOOLED,POOLED,JNDI。
如果将类型设置成UNPOOLED,MyBatis会为每一个数据库操作创建一个新的连接,使用完了并关闭它,该方式适用于只有小规模数量并发用户的简单应用程序上。
如果将属性设置成POOLED,MyBatis会创建一个数据库连接池,连接池中的一个连接将会被用作数据库操作(分配,管理,释放)。一旦数据库操作完成,MyBatis会将此连接返回给连接池。
如果将类型设置成JNDI(Java Naming and Directory Interface , Java命名和目录接口, 是SUN公司提供的一种标准的Java命名系统接口),MyBatis从在应用服务器向配置好的JNDI数据源dataSource获取数据库连接。在生产环境中,优先考虑这种方式。 - transactionManager元素JDBC事务管理器的使用,是在 JDBC程序 负责管理数据库连接的生命周期(提交、回退等等)的时候。如果将TransactionManager 属性设置成JDBC,MyBatis内部将使用JdbcTransactionFactory类创建TransactionManager。例如,部署到ApacheTomcat的应用程序,需要应用程序自己管理事务。
MANAGED 事务管理器的使用,是在 应用服务器 负责管理数据库连接生命周期的时候。如果TransactionManager属性设置成MANAGED时,MyBatis内部使用ManagedTransactionFactory类创建事务管理器TransactionManager。例如,当一个Java EE的应用程序部署在类似JBoss,WebLogic,GlassFish等应用服务器上时,它们会使用EJB进行应用服务器的事务管理能力。在这些管理环境中,你可以使用MANAGED事务管理器。 - typeAliases: 为pojo指定别名当然也可以可以不用为每一个JavaBean单独定义别名,你可以为配置出需要取别名的类的所在的包(package),MyBatis会自动扫描包内定义的类,然后分别为每个类注册一个小写字母开头的简单类名形式的别名。
- typeHandlers元素,对于mapper中传参方式#{属性名},基本数据类型和String,Date等一般mybatis使用自带的typeHandler,对于自定义类型,需要用户自定义TypeHandler进行处理。继承抽象类BaseTypeHandler,然后再mybatis-config.xml中注册
- settings元素
<settings>
<!-- 这个配置使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 允许或不允许多种结果集从一个单独的语句中返回(需要适合的驱动) -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 使用列标签代替列名。不同的驱动在这方便表现不同。参考驱动文档或充分测试两 种方法来决定所使用的驱动 -->
<setting name="useColumnLabel" value="true" />
<!-- 允许JDBC支持生成的键。需要适合的驱动。 -->
<setting name="useGeneratedKeys" value="false" />
<!-- 指定MyBatis如何自动映射列到字段/属性。PARTIAL只会自动映射简单、没有嵌 套的结果。FULL会自动映射任意复杂的结果(嵌套的或其他情况) -->
<setting name="autoMappingBehavior" value="PARTIAL" />
<!-- 配置默认的执行器。SIMPLE执行器没有什么特别之处。REUSE执行器重用预处理 语句。BATCH执行器重用语句和批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 设置超时时间,它决定驱动等待一个数据库响应的时间 -->
<setting name="defaultStatementTimeout" value="25000" />
<!-- 允许在嵌套语句中使用分页(RowBounds)默认false -->
<setting name="safeRowBoundsEnabled" value="false" />
<!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名
A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。默认false -->
<setting name="mapUnderscoreToCamelCase" value="false" />
<!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular
references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执 行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同
SqlSession 的不同调用将不会共享数据。 -->
<setting name="localCacheScope" value="SESSION" />
<!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需 要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
-->
<setting name="jdbcTypeForNull" value="OTHER" />
<!-- 指定对象的哪个方法触发一次延迟加载。 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode
,toString" />
<!-- CGLIB | JAVASSIST 默认JAVASSIST(MyBatis 3.3 or above) -->
<!-- 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 -->
<setting name="proxyFactory" value="JAVASSIST" />
<!-- 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之, 每种属性将会按需加载。 -->
<setting name="aggressiveLazyLoading" value="true" />
<!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 -->
<setting name="logImpl" value="LOG4J " />
<!-- 指定 MyBatis 增加到日志名称的前缀。值可以是任意字符串 -->
<setting name="logPrefix" value="LOG4J " />
<!-- 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为
put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类 型(int、boolean等)是不能设置成 null 的。 默认false-->
<setting name="callSettersOnNulls" value="false " />
</settings>
- mappers元素
<mappers>
<mapper resource="com/mappers/StudentMapper.xml" />
<mapper url="file:///D:/mybatisdemo/app/mappers/StudentMapper.xml" />
<mapper class="com.pojo.StudentMapper" />
<package name="com.mappers" />
</mappers>
- 创建实体类,dao接口和sql映射文件。
- 创建SqlSessionFactoryBuilder对象,需要传入mybatis-config.xml的字节流或字符流或者configuration对象,该对象的作用只有一个,那就是获取SqlSessionFactory对象,SqlSessionFactory对象的作用域为Application,即整个程序运行的阶段。调用SqlSessionFactory的openSession方法可以获取SqlSession对象。SqlSession对象的作用相当于Connection对象。使用后需要关闭连接。
映射文件的配置
insert
表示对应的接口方法为插入
如果插入对象的主键生成策略为自增长主键需要手动配置
<insert id="insertStudent" parameterType="Student"
useGeneratedKeys="true" keyProperty="studId">
INSERT INTO STUDENTS(NAME, EMAIL, PHONE)
VALUES(#{name},#{email},#{phone})
</insert>
自增长的主键会自动注入对象中的属性,但是有些数据库不支持自增长主键,比如Oracle,需要用下面这种方式。
<insert id="insertStudent" parameterType="Student">
<selectKey keyProperty="studId" resultType="int" order="BEFORE">
SELECT my_seq.nextval FROM DUAL
</selectKey>
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
VALUES(#{studId},#{name},#{email},#{phone})
</insert>
update
表示对应的接口方法为修改
delete
表示对应的接口方法为删除
select
表示对应的接口方法为查询
传入参数 parameterType和resultType
MyBatis强大之处在于映射SELECT查询结果到java的各种类型。包括Collection和Map.
对于List,Collection,Iterable类型,MyBatis将返java.util.ArrayList
对于Map类型,MyBatis 将返回java.util.HashMap
对于Set类型,MyBatis 将返回java.util.HashSet
对于SortedSet类型,MyBatis将返回java.util.TreeSet
ResultMap:
ResultMap被用来将SELECT语句的结果集映射到java对象的属性中。我们可以定义结果集映射ResultMaps并且在一些SELECT语句上引用resultMap。MyBatis的结果集映射 ResultMaps特性非常强大,你可以使用它将简单的SELECT语句映射到复杂的一对一、一对多关系的SELECT语句上。
resultMap的id值应该在此名空间内是唯一的,并且type属性是完全限定类名或者是返回类型的别名。< result >子元素被用来将一个resultset列映射到对象的一个属性中。< id >元素和< result >元素功能相同,不过< id >它被用来映射到唯一标识属性,用来区分和比较对象(一般和主键列相对应)
<resultMap id="StudentResult" type="com.pojo.Student">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
拓展resultMap,有时resultMap中映射的元素不够,而我们为了节省代码量,可以使用resultMap的extends属性,拓展出一个新的resultMap。
一对一映射
- 使用嵌套resultMap
<!-- 独立的Address封装映射 -->
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<!-- Student封装映射,里面关联上Address的封装映射 -->
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="dob" column="dob" />
<result property="phone" column="phone" />
<association property="address" resultMap="AddressResult" />
</resultMap>
< association > 是关联的意思,常被用来表示(has-one)类型的关联。就是对象1里面关联另一个对象2
- 使用嵌套查询
<!-- 独立的Address封装映射 -->
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<!-- 独立的select查询,专门查询Address -->
<select id="findAddressById" parameterType="int" resultMap="AddressResult">
select * from addresses
where addr_id=#{id}
</select>
<!-- Student封装映射,里面关联了查询address使用的select语句,并指定数据库表中的 这个关联的外键列的名字,这里是addr_id -->
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="dob" column="dob" />
<result property="phone" column="phone" />
<association property="address" column="addr_id"
select="findAddressById" />
</resultMap>
<!-- 查询Student的select语句,这里不用写多表查询,因为对于address的关 联查询,已经在上边定义好了,并且在结果映射中关联进来了 -->
<select id="selectStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
select * from students
where stud_id=#{id}
</select>
在此方式中, < association >元素的select属性被设置成了id为findAddressById的语句。两个分开的SQL语句将会在数据库中分别执行,第一个通过id查询student信息,而第二个调用findAddressById来加载address信息。addr_id列的值将会被作为输入参数传递给selectAddressById语句作为参数进行条件查询。
一对多映射
< collection > 标签可以用来将多行课程结果映射成一个课程Course对象的集合。和之前的一对一映射一样,可以使用【嵌套结果】ResultMap和【嵌套查询】Select语句两种方式映射实现一对多映射。
- 使用嵌套结果
<!-- 独立的Address封装映射 -->
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<!-- 独立的Course封装映射 -->
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<!-- Tutor封装映射,里面是有嵌套结果的方式关联一个Addres和多个Course,分别 使用association标签和collection标签 -->
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="name" property="name" />
<result column="email" property="email" />
<result column="phone" property="phone" />
<association property="address" resultMap="AddressResult" />
<collection property="courses" resultMap="CourseResult" />
</resultMap>
<!-- 查询Tutor的select语句,多表连接查询,把查询的结果给个上面的映射结果统 一进行封装 -->
<select id="findTutorById" parameterType="int"
resultMap="TutorResult">
select
t.tutor_id, t.name, t.email,t.phone,
c.course_id, c.name, c.description, c.start_date,
c.end_date,
a.addr_id,a.street,a.city,a.state,a.zip,a.country
from
tutors t
left outer join addresses a on t.addr_id = a.addr_id
left outer join courses c on t.tutor_id = c.tutor_id
where
t.tutor_id=#{id}
</select>
- 使用嵌套查询
<!-- 独立的Address封装映射 -->
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<!-- 独立的Course封装映射 -->
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<!-- Tutor封装映射,里分别使用association和collection标签来进行关联查询, 直接把对应的select查询语句管理进行 -->
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="tutor_name" property="name" />
<result column="email" property="email" />
<result column="phone" property="phone" />
<!-- 把addr_id列的值当做参数传给findAddressById进行查询 -->
<association property="address" column="addr_id"
select="findAddressById"></association>
<!-- 把tutor_id列的值当做参数传给findCoursesByTutor进行查询 -->
<collection property="courses" column="tutor_id"
select="findCoursesByTutor" />
</resultMap>
<!-- 单独的Tutor查询语句 -->
<select id="findTutorById" parameterType="int"
resultMap="TutorResult">
select *
from tutors
where tutor_id=#{tutor_id}
</select>
<!-- 单独的Address查询语句 -->
<select id="findAddressById" parameterType="int"
resultMap="AddressResult">
select *
from addresses
where addr_id = #{addr_id}
</select>
<!-- 单独的Course查询语句 -->
<select id="findCoursesByTutor" parameterType="int"
resultMap="CourseResult">
select *
from courses
where tutor_id=#{tutor_id}
</select>
多对多映射
<?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.mappers.Many2ManyMapper">
<insert id="insertStudent"
parameterType="com.many2many.Student">
<selectKey keyProperty="id" resultType="int"
order="BEFORE">
select my_seq.nextval from dual
</selectKey>
insert into
student(id,name,gender,major,grade)
values
(#{id},#{name},#{gender},#{major},#{grade})
</insert>
<insert id="insertCourse"
parameterType="com.many2many.Course">
<selectKey keyProperty="id" resultType="int"
order="BEFORE">
select my_seq.nextval from dual
</selectKey>
insert into
course(id,course_code,course_name)
values
(#{id},#{courseCode},#{courseName})
</insert>
<select id="getStudentById" parameterType="int"
resultType="com.many2many.Student">
select id,name,gender,major,grade
from student
where id=#{id}
</select>
<select id="getCourseById" parameterType="int"
resultType="com.many2many.Course">
select id,course_code as courseCode,course_name as
courseName
from course
where id=#{id}
</select>
<!-- param1代表方法中第一个参数 以此类推 -->
<insert id="studentSelectCourse">
insert into
student_course(id,student_id,course_id)
values
(my_seq.nextval,#{param1.id},#{param2.id})
</insert>
<!-- 如果有特殊符号的话 需要用 <![CDATA[ 特殊符号 ]]> 例如 < & 等等 -->
<select id="getStudentByIdOnCondition" parameterType="int"
resultType="com.many2many.Student">
select *
from student
where id <![CDATA[ < ]]>
#{id}
</select>
<!-- 这里使用了嵌套结果ResultMap的方式进行级联查询 当然也可以使用嵌套查 询select -->
<!-- 映射一个基本的Student查询结果 -->
<resultMap id="StudentResult"
type="com.many2many.Student">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="gender" column="gender" />
<result property="major" column="major" />
<result property="grade" column="grade" />
</resultMap>
<!-- 继承上面那个基本的映射,再扩展出级联查询 -->
<resultMap id="StudentResultWithCourses"
type="com.many2many.Student"
extends="StudentResult">
<collection property="courses" resultMap="CourseResult">
</collection>
</resultMap>
<!-- 这里特别注意的是column="cid" 这是和select语句中的 c.id as cid对 应的 -->
<resultMap id="CourseResult" type="com.many2many.Course">
<id property="id" column="cid" />
<result property="courseCode" column="course_code" />
<result property="courseName" column="course_name" />
</resultMap>
<!-- 注意:查询语句的中的c.id as cid这个地方,避免名字相同出现查询结果不 正确的情况 同时在id="CourseResult"的resultMap中也有与这里对应的设置要特别特别 注意 -->
<select id="getStudentByIdWithCourses" parameterType="int"
resultMap="StudentResultWithCourses">
select s.id,s.name,s.gender,s.major,s.grade,c.id as
cid,c.course_code,c.course_name,sc.id,sc.student_id,sc.course_id
from student s,course c,student_course sc
where
s.id=#{id}
and
s.id = sc.student_id
and
c.id = sc.course_id
</select>
</mapper>
ORM全称对象关系映射,即在对象模型(JavaBean)和关系型数据库之间建立起对应的联系 ↩︎