我们对MyBatis已经有了初步的了解,但这些只是基础,还远远不够,我们还需要对MyBatis中的核心对象、映射文件和配置文件有更加深入的了解。
1. MyBatis的核心对象
1. 1 SqlSessionFactory
SqlSessionFactory是MyBatis中的关键对象,它是单个数据库映射关系经过编译后的内存镜像,其主要作用是创建SqlSession。SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来构建,而SqlSessionFactoryBuilder则可以通过XML配置文件或预先定义好的Configuration实例构建出SqlSessionFactory的实例。
// 读取配置文件
String resource = "mybatis-config.xml";
InputStream inputStream =
Resources.getResourceAsStream(resource);
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactory对象是线程安全的,它一旦被创建,在整个应用执行期间都会存在。
如果我们多次创建同一个数据库的SqlSessionFactory,那么此数据库的资源将很容易被耗尽。为此,通常每一个数据库都会只对应一个SqlSessionFactory,所以在构建SqlSessionFactory实例时,建议使用单列模式。
1. 2 SqlSession
SqlSession是MyBatis中另一个关键对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作。SqlSession对象包含了数据库中所有执行SQL操作的方法,其底层封装了JDBC连接,可以直接使用其实例来执行已映射的SQL语句。
每个线程都应该有自己的SqlSession实例,并且SqlSession的实例是不能被共享的,SqlSession实例也是线程不安全的。因此最佳的范围是一次请求或一个方法中,绝对不能将其放在一个类的静态字段、实例字段或任何类型的管理范围(如Serlvet的HttpSession)中。 使用完SqlSession对象后要及时关闭,通常可以将其放在finally块中关闭。
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 此处执行持久化操作
} finally {
sqlSession.close();
}
SqlSession中常用的方法:
查询方法:
方法 | 说明 |
---|---|
参数statement是在配置文件中定义的 | |
参数statement是在配置文件中定义的 | |
参数statement是在配置文件中定义的 | |
参数statement是在配置文件中定义的 | |
参数statement是在配置文件中定义的 | |
void select(String statement, Object parameter, ResultHandler handler); | 参数statement是在配置文件中定义的 |
插入、更新和删除方法:
方法 | 说明 |
---|---|
int insert(String statement); | 参数statement是在配置文件中定义的 |
int insert(String statement, Object parameter); | 参数statement是在配置文件中定义的 |
int update(String statement); | 参数statement是在配置文件中定义的 |
int update(String statement, Object parameter); | 参数statement是在配置文件中定义的 |
int delete(String statement); | 参数statement是在配置文件中定义的 |
int delete(String statement, Object parameter); | 参数statement是在配置文件中定义的 |
其他方法:
方法 | 说明 |
---|---|
void commit(); | 提交事务的方法。 |
oid rollback(); | 回滚事务的方法。 |
void close(); | 关闭SqlSession对象。 |
返回Mapper接口的代理对象。 | |
Connection getConnection(); | 获取JDBC数据库连接对象的方法。 |
2. 配置文件
在MyBatis框架的核心配置文件中,
2.1.
(1).编写db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
(2).配置
<properties resource="db.properties" />
(3).修改配置文件中数据库连接的信息
<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>
2.2.
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" value="true" />
<setting name="multipleResultSetsEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="useGeneratedKeys" value="false" />
<setting name="autoMappingBehavior" value="PARTIAL" />
...
settings>
2.3.
(1). 使用元素配置别名的方法如下:
<typeAliases>
<typeAlias alias="user" type="com.example.test.User"/>
typeAliases>
(2). 当POJO类过多时,可以通过自动扫描包的形式自定义别名,具体如下:
<typeAliases>
<package name="com.example.test"/>
typeAliases>
MyBatis框架默认为许多常见的Java类型提供了相应的类型别名,如下表所示:
2.4.
typeHandler的作用就是将预处理语句中传入的参数从javaType(Java类型)转换为jdbcType(JDBC类型),或者从数据库取出结果时将jdbcType转换为javaType。
(1).注册一个类的类型处理器
<typeHandlers>
<typeHandler handler="com.example.test.UsertypeHandler" />
typeHandlers>
(2).注册一个包中所有的类型处理器
<typeHandlers>
<package name="com.itheima.test" />
typeHandlers>
2.5.
MyBatis中默认的ObjectFactory的作用是实例化目标类,它既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。通常使用默认的ObjectFactory即可。
2.6.
MyBatis允许在已映射语句执行过程中的某一点进行拦截调用,这种拦截调用是通过插件来实现的。
2.7.
使用
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<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>
2.7.1 事务管理器的配置:
在MyBatis中,可以配置两种类型的事务管理器,分别是JDBC和MANAGED。关于这两个事务管理器的描述如下:
1. JDBC: 此配置直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
2. MANAGED:此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但一些容器并不希望这样,为此可以将closeConnection属性设置为false来阻止它默认的关闭行为。
注意: 如果项目中使用的是Spring+ MyBatis,则没有必要在MyBatis中配置事务管理器,因为实际开发中,会使用Spring自带的管理器来实现事务管理。
2.7.2 数据源的配置:
1.UNPOOLED
配置此数据源类型后,在每次被请求时会打开和关闭连接。它对没有性能要求的简单应用程序是一个很好的选择。在使用时,需要配置5种属性。
属性 | 说明 |
---|---|
driver | JDBC驱动的Java类的完全限定名(并不是JDBC驱动中可能包含的数据源类) |
url | 数据库的URL地址 |
username | 登录数据库的用户名 |
password | 登录数据库的密码 |
defaultTransactionIsolationLevel | 默认的连接事务隔离级别 |
2.POOLED
此数据源利用“池”的概念将JDBC连接对象组织起来,避免了在创建新的连接实例时所需要初始化和认证的时间。这种方式使得并发Web应用可以快速的响应请求,是当前流行的处理方式。在使用时,可以配置更多的属性。
属性 | 说明 |
---|---|
poolMaximumActiveConnections | 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10 |
poolMaximumIdleConnections | 任意时间可能存在的空闲连接数 |
poolMaximumCheckoutTime | 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000毫秒,即20秒 |
poolTimeToWait | 如果获取连接花费的时间较长,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直处于无 提示的失败),默认值: 20000毫秒,即20秒 |
poolPingQuery | 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中。默认是“NO PING QUERY SET",这会导致多数数据库驱动失败时带有一定的错误消息 |
poolPingEnabled | 是否启用侦测查询。若开启,必须使用一个可执行的SQL语句设置,poolPingQuery属性(最好是一个非常快的SQL),默认值: false |
poolPingConnectionsNotUsedFor | 配置poolPingQuery的使用频度。可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值: 0 (表示所有连接每一时刻都被侦测,只有poolPingEnabled的属性值为true时适用) |
3.JNDI
可以在EJB或应用服务器等容器中使用。容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。在使用时,需要配置2个属性。
属性 | 说明 |
---|---|
initial_context | 此属性主要用于在InitialContext中寻找上下文(即initialContext.lookup(initial_context))。该属性为可选属性,在忽略时,data_source属性会直接从InitialContext中寻找 |
data_source | 此属性表示引用数据源实例位置的上下文的路径。如果提供了initial_context配置,那么程序会在其返回的上下文中进行查找;如果没有提供,则直接在InitialContext中查找 |
2.8.
(1).使用类路径引入
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
mappers>
(2).使用本地文件路径引入
<mappers>
<mapper url="file:///D:/com/example/mapper/UserMapper.xml"/>
mappers>
(3).使用接口类引入
<mappers>
<mapper class="com.itheima.mapper.UserMapper"/>
mappers>
(4).使用包名引入
<mappers>
<package name="com.itheima.mapper"/>
mappers>
3. 映射文件
在映射文件中,元素是映射文件的根元素,其他元素都是它的子元素。
3.1.
属性 | 说明 |
---|---|
id | 表示命名空间中的唯一标识符,常与命名空间组合起来使用。组合后如果不唯一,MyBatis会抛出异常 |
parameterType | 该属性表示传入SQL语句的参数类的全限定名或者别名。它是一个可选属性,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数。其默认值是unset (依赖于驱动) |
resultType | 从SQL语句中返回的类型的类的全限定名或者别名。如果是集合类型,那么返回的应该是集合可以包含的类型,而不是集合本身。返回时可以使用resultType或resultMap之一 |
resultMap | 表示外部resultMap的命名引用。返回时可以使用resultType或resultMapresultMap之一 |
useCache | 用来控制二级缓存的开启和关闭。其值为布尔类型(true/false), 默认值为true,表示将查询结果存入二级缓存中 |
timeout | 用于设置超时参数,单位为秒。超时时将抛出异常 |
fetchSize | 获取记录的总条数设定,其默认值是unset (依赖于驱动) |
resultSetType | 表示结果集的类型,其值可设置为FORWARD_ONLY、SCROLL_SENSITIVE或SCROLL_INSENSITIVE, 它的默认值是unset(依赖于驱动) |
<select id="findUesrById" parameterType="Integer"
resultType="com.example.po.User">
select * from user where id = #{id}
select>
3.2.
属性 | 说明 |
---|---|
keyProperty | (仅对insert和update有用)此属性的作用是将插入或更新操作时的返回值赋值给PO类的某个属性,通常会设置为主键对应的属性。如果需要设置联合主键,可以在多个值之间用逗号隔开 |
keyColumn | (仅对 insert和update有用)此属性用于设置第几列是主键,当主键列不是表中的第一列时需要设置。在需要主键联合时,值可以用逗号隔开 |
useGeneratedKeys | (仅对insert和update有用)此属性会使MyBatis使用JDBC的getGeneratedKeys()方法来获取由数据库内部生产的主键,如MySQL和SQL Server等自动递增的字段,其默认值为false |
执行插入操作后,若需要返回插入成功的数据生成的主键值,此时通过上面3个属性来实现。
1.对于支持主键自助增长的数据库(如MySQL),可以通过如下配置实现:
<insert id="addUser" parameterType="com.example.po.User"
keyProperty="id" useGeneratedKeys="true" >
insert into user(username,jobs,phone)
values(#{username},#{jobs},#{phone})
insert>
2.对于不支持主键自助增长的数据库(如Oracle),可以通过如下配置实现:
<insert id="insertUser" parameterType="com.example.po.User">
<selectKey keyProperty="id" resultType="Integer" order="BEFORE">
select if(max(id) is null, 1, max(id) +1) as newId from t_customer
selectKey>
insert into t_user(id,username,jobs,phone)
values(#{id},#{username},#{jobs},#{phone})
insert>
3.3.
<update
id="updateUser"
parameterType="com.example.po.User"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteUser"
parameterType="com.example.po.User"
flushCache="true"
statementType="PREPARED"
timeout="20">
<update id="updateUser" parameterType="com.example.po.User">
updateuser
set username=#{username},jobs=#{jobs},phone=#{phone}
where id=#{id}
update>
<delete id="deleteUser" parameterType="Integer">
delete from user where id=#{id}
delete>
3.4.
定义一个包含id、username、jobs和phone字段的代码片段如下:
<sql id="userColumns">id,username,jobs,phonesql>
上述代码片段可以包含在其他语句中使用,具体如下:
<select id="findUserById" parameterType="Integer"
resultType="com.example.po.User">
select <include refid="userColumns"/>
from user
where id = #{id}
select>
3.5.
<resultMap type="" id="">
<constructor>
<idArg/>
<arg/>
constructor>
<id/>
<result/>
<association property="" />
<collection property="" />
<discriminator javaType="">
<case value="" />
discriminator>
resultMap>
☆☆☆Eclipse实现数据库中表的增、删、查、改。
1.在Eclipse中,创建项目,将所需要的JAR包复制到项目中的WebContent的WEB-INF的lib下,并发布到类路径下。
2.在src目录下,创建db.properties,log4j.properties文件,以及mybatis-config.xml文件。
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/d1?useSSL=false
jdbc.username=root
jdbc.password=root
log4j.properties
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger=DEBUG
# 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
mybatis-config.xml
<configuration>
<properties resource="db.properties" />
<typeAliases>
<package name="beans" />
typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<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>
<mapper resource="mapper/StudentMapper.xml" />
mappers>
configuration>
3.在src中创建beans包,在beans包中创建Student类,创建成员变量和setXXX、getXXX方法。
package beans;
import java.io.Serializable;
import java.text.DateFormat;
import java.util.Date;
public class Student implements Serializable{
/*serialVersionUID作用:
序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。 */
private static final long serialVersionUID = 1L;
private Integer id;
private String stuno;
private String name;
private Date birthday;
private String phone;
private float score;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStuno() {
return stuno;
}
public void setStuno(String stuno) {
this.stuno = stuno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
@Override
public String toString() {
return "id=" + id + ", stuno=" + stuno +
", name=" + name + ", birthday="
+ DateFormat.getDateInstance().format(birthday)
+ ", phone=" + phone + ", score=" + score;
}
}
4.在src中创建tools包,在tools包中创建工具类,方便使用。
package tools;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* 工具类
*/
public class DBTools {
private static SqlSessionFactory sqlSessionFactory = null;
// 初始化SqlSessionFactory对象
static {
try {
// 使用MyBatis提供的Resources类加载MyBatis的配置文件
Reader reader =
Resources.getResourceAsReader("mybatis-config.xml");
// 构建SqlSessionFactory工厂
sqlSessionFactory =
new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取SqlSession对象的静态方法
public static SqlSession getSession() {
return sqlSessionFactory.openSession();
}
}
5.在src中创建mapper包,在mapper包中创建映射文件StudentMapper.xml,以及在mybatis-config.xml中配置Mapper的位置(在上面mybatis-config.xml已配置)。
<mapper namespace="mapper.StudentMapper">
<resultMap type="beans.Student" id="resultMap">
<id property="id" column="id"/>
<result property="stuno" column="stuno"/>
<result property="name" column="name"/>
<result property="birthday" column="birthday" jdbcType="DATE" javaType="java.util.Date"/>
<result property="phone" column="phone"/>
<result property="score" column="score"/>
resultMap>
<select id="getStudent" resultMap="resultMap">
select * from t_stu
select>
<insert id="addStudent" parameterType="beans.Student"
keyProperty="id" useGeneratedKeys="true">
insert into t_stu(stuno,name,birthday,phone,score)
values(#{stuno},#{name},#{birthday,jdbcType=DATE},#{phone},#{score})
insert>
<update id="updateStudent" parameterType="beans.Student">
update t_stu
set stuno=#{stuno},name=#{name},birthday=#{birthday},phone=#{phone},score=#{score}
where id=#{id}
update>
<delete id="deleteStudent" parameterType="Integer">
delete from t_stu where id=#{id}
delete>
mapper>
6.在src中创建test包,在test包包中创建测试类Test类。
查询操作:
package test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import beans.Student;
import tools.DBTools;
public class test {
@Test
public void getStudentTest(){
// 获取SqlSession
SqlSession sqlSession = DBTools.getSession();
// SqlSession执行映射文件中定义的SQL,并返回映射结果
List<Student> list =
sqlSession.selectList("mapper.StudentMapper.getStudent");
// 打印输出结果
for(Student student : list){
System.out.println(student);
}
// 关闭SqlSession
sqlSession.close();
}
}
package test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import beans.Student;
import tools.DBTools;
public class test {
@Test
public void insertStudentTest() throws ParseException{
SqlSession sqlSession = DBTools.getSession();
Student student = new Student();
student.setStuno("202");
student.setName("lisi");
student.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1998-10-20"));
student.setPhone("123456789");
student.setScore(90);
int rows = sqlSession.insert("mapper.StudentMapper.addStudent", student);
System.out.println(student.getId());
if(rows > 0){
System.out.println("您成功插入了"+rows+"条数据!");
}else{
System.out.println("执行插入操作失败!!!");
}
sqlSession.commit();
sqlSession.close();
}
}
package test;
import java.text.ParseException;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import beans.Student;
import tools.DBTools;
public class test {
@Test
public void updateStudentTest() throws ParseException{
SqlSession sqlSession = DBTools.getSession();
// SqlSession执行更新操作
// 创建Customer对象,并向对象中添加数据
Student student = new Student();
student.setId(3);
student.setStuno("1004");
student.setName("zhaoliu");
//student.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1997-07-07"));
//student.setPhone("123456789");
student.setScore(90);
// 执行sqlSession的更新方法,返回的是SQL语句影响的行数
int rows = sqlSession.update("mapper.StudentMapper.updateStudent", student);
// 通过返回结果判断更新操作是否执行成功
if(rows > 0){
System.out.println("您成功修改了"+rows+"条数据!");
}else{
System.out.println("执行修改操作失败!!!");
}
// 提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
}
运行结果:
表中的变化如下,可以看到对name、birthday、phone未赋值,表的数据变为null,使用动态sql(关于动态sql下次会详细说),则运行后,即使未赋值也不会变为null。
删除操作:
package test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import tools.DBTools;
public class test {
@Test
public void deleteStudentTest() {
// 获取SqlSession
SqlSession sqlSession = DBTools.getSession();
// SqlSession执行删除操作
// 执行SqlSession的删除方法,返回的是SQL语句影响的行数
int rows = sqlSession.delete("mapper.StudentMapper.deleteStudent", 3);
// 通过返回结果判断删除操作是否执行成功
if(rows > 0){
System.out.println("您成功删除了"+rows+"条数据!");
}else{
System.out.println("执行删除操作失败!!!");
}
// 提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
}
运行结果:
这就是MyBatis核心配置的简单使用,如果转载以及CV操作,请务必注明出处,谢谢!