MyBatis(一)
目录
1.MyBatis基本用法
2.JDBC编程问题
3.MyBatis简介
3.1 MyBatis与Hibernate的区别
3.2 MyBatis的作用及特点
4.MyBatis入门案例
4.1 配置MyBatis运行环境
pom文件
log4j.properties配置文件
sqlMapConfig.xml配置文件
4.2 根据ID查询用户
mapper.xml配置文件
sqlMapConfig.xml配置文件
测试程序
4.3 根据用户名查询
mapper.xml配置文件
测试代码
mapper.xml配置文件
测试代码
4.4 用户添加操作
mapper.xml配置文件
测试代码
4.5 返回自增长主键
mapper.xml配置文件
测试程序
4.6 返回非自增长主键
mapper.xml配置文件
4.7 删除用户操作
mapper.xml配置文件
测试程序
4.8 更新用户操作
mapper.xml配置文件
测试程序
5.#{}与${}的区别
6.selectOne与selectList
1.MyBatis基本用法
MyBatis是一个简化了 Java 数据持久化层(persistence layer)代码的开源框架,它抽象了大量的JDBC冗余代码,并提供了一个简单、易用的API与数据库进行交互。
MyBatis的前身是ibatis,ibatis于2002年由克林顿.比让(Clinton Begin)创建。MyBatis是ibatis的全新设计,并加入了注解和高级Mapper代理,是当下非常流行的持久层框架。
MyBatis流行的主要原因在于它的简单性和易用性。在Java应用程序中,数据持久化层涉及到的操作为:将从数据库查询到的数据映射成所对应的Java对象;将Java对象中的数据通SQL持久化到数据库中。
MyBatis通过抽象底层的JDBC代码,可以将SQL查询结果集自动映射到Java对象,将Java对象的数据持久化通过SQL自动存储到数据库中,让整个Dao层的开发过程转变成对SQL的操作,使JDBC编程变得非常容易。
2.JDBC编程问题
在Java EE应用开发中,必不可免的要与数据库打交道,与数据库的交互一般都是通过定一个DAO层(数据库访问)来完成这个操作。定义DAO层的主要是原因是为了解决在开发过程中业务与数据的分离,可以在不同的层次上解决不同的问题。从而避免让所有代码冗余在一起,更有利于后期应用的升级与维护。
但是,即使在应用开发中实现了分层,使用单纯的JDBC编程仍然存在大量的问题。如下代码所示:
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
String sql = "select * from user where user_name=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "zhangsan");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
User us = new User();
us.setUserId(rs.getInt("user_id"));
us.setUsername(rs.getString("user_name"));
us.setPassword(rs.getString("user_pass"));
us.setCname(rs.getString("cname"));
}
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
1、数据库的连接,使用时就需要连接,不需要就关闭,频繁的打开与关闭连接,势必会造成数据库资源的浪费,降低数据库的性能。
解决方案:数据库连接池。
2、将要执行的SQL语句以硬编码的方式写入到程序中,如果需求发生变动,则需要更改代码,不利于系统的维护。
解决方案:将SQL语句配置在XML文件中,即使SQL语句更改,也不需要更改Java源程序。
3、如果执行的SQL中含有占位符,则需要向执行SQL语句的PreparedStatement对象中传入参数,如果查询条件发生更改,则占位符的数目也需要改动,那么传入的参数个数会随之发生变动。
解决方案:将执行SQL语句中的占位符、参数,同样配置在XML文件中。
4、执行查询后得到的ResultSet结果集中,在获取数据时将数据表的字段名称,同样以硬编码的方式写死在程序中,不利于系统的维护。
解决方案:通过配置将查询的结果集,自动映射为对应的Java对象。
以上所有问题的解决,就是数据库持久层框架存在的意义,而MyBatis这个数据库持久层框架就很好的解决了以上所有的问题。
3.MyBatis简介
MyBatis本是apache下的一个开源项目,项目的前身为iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并正式更名为MyBatis 。MyBatis是一个支持普通 SQL查询、存储过程、高级映射的优秀持久层开源框架。MyBatis几乎消除JDBC的所有代码,其中包括:参数的手动设置代码,对查询结果集进行遍历的代码。MyBatis 使用简单的XML或注解的方式用于配置数据和接口映射,将Java 的POJO(Plain Ordinary Java Objects,普通的Java对象)与数据库中对应的记录进行相互映射。
3.1 MyBatis与Hibernate的区别
Mybatis和Hibernate不同,它不是一个全自动ORM框架,因为MyBatis需要程序员自己编写SQL语句,不过Mybatis可以通过XML或注解方式灵活配置要运行的SQL语句,并将Java对象中的数据和SQL语句映射生成最终执行的SQL,最后将SQL执行的结果再映射生成Java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态SQL,可严格控制SQL执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化就会要求成果输出迅速。但是灵活的前提是MyBatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套SQL映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用Hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只
有适合才是最好。
3.2 MyBatis的作用及特点
MyBatis是一个持久层框架,它的主要作用就是可以让程序开发人员将主要精力放在SQL语句的编写上,通过MyBatis提供的映射方式,自由灵活的创建出满足业务需求的各种SQL语句,不管是单表查询,还是多表查询,都可以很好的完成。而这种灵活生成是一种半自动化的生成,即还是需要程序人员进行编写SQL。
如果要执行的SQL语句中存在占位符,那么JDBC在执行之前,需要向执行该SQL语句的PreparedStatement对象传入参数。Mybatis可以将参数进行自动的输入映射,查询之后的结果集,Mybatis可以灵活的映射成Java对象,这就是Mybatis最为重要的输入映射和输出映射。
如果使用MyBatis框架,则需要去它的官方网站下载,目前最新版本为3.4.1版,该地址为:
https://github.com/mybatis/mybatis-3/releases
下面来看一下MyBatis的结构示意图,如下图所示:
4.MyBatis入门案例
下面使用MyBatis框架来做一个简单的入门案例,以用户表为例,通过MyBatis框架完成最简单的增、删、改、查操作。
4.1 配置MyBatis运行环境
首先创建一个简单的Maven工程,并在Maven工程的pom文件中,加入MyBatis框架的依赖。因为MyBatis框架是一个持久层框架,需要使用数据库的驱动包,以MySQL数据库为例进行配置,所以同时加入MySQL的驱动包依赖。MyBatis框架的运行还依赖日志输出工具包Log4J,因此还要引入Log4J的依赖。pom文件内容如下:
pom文件
<!-- 引入MyBatis的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- 引入MySql的依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- 引入log4j的依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Log4J框架的运行需要依赖一个配置文件,用于指定日志输出的位置、消息布局、消息格式等内容。Log4J框架采用约定优于配置的策略,明确指定配置文件的名称为log4j,配置文件可以采用xml格式或者properties格式,推荐使用properties格式,配置文件必须放在类加载路径下。对于log4j配置文件,单独详细讲解。Log4J的配置文件的内容如下。
log4j.properties配置文件
# 定义输出级别为debug,输出名称为stdout
log4j.rootLogger=debug,stdout
# 定义stdout的输出采用哪个类来执行
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 定义stdout的输出类型的样式布局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义stdout样式布局的消息格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
MyBatis框架运行需要配置一个全局配置文件sqlMapConfig.xml,此配置文件名称不固定,可由开发人员自己
指定,可以将配置文件放在resources目录下,配置内容如下所示:
sqlMapConfig.xml配置文件
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- MyBatis的全局参数设置,用来进行MyBatis的优化处理 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 以下配置不需掌握,和Spring整合后,以下配置将被废除 -->
<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?useUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
-
4.2 根据ID查询用户
MyBatis框架几乎消除了JDBC的所有代码,并提供XML配置或注解的方式来映射接口和POJO,下面以XML的方式来配置JDBC层的操作。MyBatis框架提供mapper.xml文件用于配置要执行的SQL操作,并将执行SQL时要传入的参数,以及传出的参数完成了映射。下面列出包含根据ID查询用户的mapper文件,内容如下。
mapper.xml配置文件
文件名为user-mapper.xml
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="user">
<select id="findUserById" parameterType="int" resultType="com.langsin.user.pojo.User">
select * from user where user_id=#{userId}
</select>
</mapper>
mapper.xml文件的根标签为<mapper>,<mapper>标签提供给了namespace属性用于指定命名空间,用于区分不同mapper文件中的SQL,非常类似于Java中pageckage包的概念。namespace属性值可以自己定义,但如果后续使用mapper高级代理的方式,namespace属性值则有特殊的作用。
<select>标签用于查询操作,id属性为select的唯一标识,可以看成是在dao层定义的方法名,属性parameterType是输入映射,表示执行SQL时要接收的参数的数据类型,参数类型可以是Java的各种数据类型,resultType是执行SQL语句后,将查询结果集中的每条记录映射成的输出类型,可以是Java中的各种类型,如果是一个Java Bean类,则需要指定Java Bean的全限定名。
<select>标签中包含了要执行的SQL语句,SQL语句中如果需要传参,则使用占位符的方式,在MyBatis框架中使用#{}的方式表示占位符,其中大花括号中填写的是对应的参数值,如果是基本数据类型,可以填写任意名称,但是应该遵循开发规范写参数名,比如:userId。
MyBatis框架解析mapper文件时,会将配置在内的SQL语句封装成一个可以执行的MappedStatement对象,并将这些对象在MyBatis框架中进行管理。程序执行时,需要将mapper文件整合在全局配置文件sqlMapConfig.xml文件中,全配置文件中提供了<mappers>标签将程序中的mapper文件整合在一起。配置如下:
sqlMapConfig.xml配置文件
<!-- 在全局配置文件中引入mapper文件 -->
<mappers>
<mapper resource="mapper/user-mapper.xml"/>
</mappers>
测试程序
如下:
@Test
public void testFindUserById() throws Exception {
// 读取全局配置文件
InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 创建一个SqlSession会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
// 创建会话对象
SqlSession session = factory.openSession();
// 执行查询操作
User user = session.selectOne("user.findUserById", 11);
System.out.println(user.getUserId());
}
程序解析:在上述程序中,通过SqlSession对象的selectOne方法进行查询单条记录,此方法需要两个参数,第一个参数为statementId,在配置mapper.xml文件时,<select>标签的id属性为SQL语句的标识,但是mapper文件中配置的SQL在执行时,最终会被MyBatis封装到一个MappedStatement对象中去,而这个id也就是MappedStatement对象的id,这个参数的字符格式为:命名空间+“.”+属性ID的方式,例如:user.findUserById。
注意:在创建对应的JavaBean时,MyBatis默认是以数据库表中字段列名与Java Bean中的成员变量名进行对应的,所以在定义成员变量名是需要与表字段的列名保持一致。
4.3 根据用户名查询
在用户查询操作中,如果根据用户名进行查询,大多数情况下都是进行模糊查询操作,在MyBatis中如果进行模糊查询操作,只需要将SQL语句更改为模糊查询即可。如下所示:
mapper.xml配置文件
<select id="findUserByName" parameterType="String" resultType="com.langsin.user.pojo.User">
select * from user where cname like '%${value}%'
</select>
注意:在上述的SQL语句中没有使用#{}占位符的方式,因为这里进行模糊匹配查询要用到%号,要进行字符串拼接,所以使用了 '%${value}%'。因为传入的参数为String类型,是引用类型,不是基本数据类型所以大括号中只能使用value,而不能使用其他变量名。使用${value}的方式,表示对参数不做任何修饰直接进行SQL拼接,但这样做具有一定的危险性,会导致程序安全性能降低,原因就是SQL注入,就是因为使用了字符串拼接。因此不建议使用。
测试代码
@Test
public void testFindUserByName() throws Exception {
InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
SqlSession session = factory.openSession();
List<User> userList = session.selectList("user.findUserByName", "李");
for (User user : userList) {
System.out.println(user.getCname());
}
}
程序解析:模糊查询的结果可能不止一条记录,所以不能再使用SqlSession对象的selectOne方法,该方法只能将查询结果为一条记录的结果集进行转换,而如果是多条则会抛出异常。可以使用selectList方法,此方法可以将查询结果集映射为一个集合,返回一个List集合。
将上面程序进行改造,更改为使用#{}占位符的方式,只不过需要程序开发人员在对传入的参数进行处理,更改为“%参数%”的方式。
mapper.xml配置文件
<select id="findUserByName" parameterType="String" resultType="com.langsin.user.pojo.User">
select * from user where cname like #{value}
</select
测试代码
@Test
public void testFindUserByName() throws Exception {
InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
SqlSession session = factory.openSession();
List<User> userList = session.selectList("user.findUserByName", "%李%");
for (User user : userList) {
System.out.println(user.getCname());
}
}
-
4.4 用户添加操作
使用MyBatis进行插入操作与查询操作类似,都是相同的流程,只不过在mapper.xml配置文件中使用的标签不同,调用的SqlSession的方法不同而已。添加数据使用<insert>标签,对应的是SqlSession对象的insert()方法。使用<insert>标签添加一条记录时,需要传入多个参数,此时可以使用Java Bean对象,把多个参数封装在Java Bean中,再把Java Bean对象作为参数整体传入。在SQL语句中,占位符的大括号中,填写的就是Java Bean对象的成员变量名。
mapper.xml配置文件
<!-- 配置insert语句 -->
<insert id="addUser" parameterType="com.langsin.user.pojo.User" >
insert into user values(null, #{cname}, #{password}, #{username}, #{money})
</insert>
测试代码
@Test
public void testAddUser() throws Exception{
User user = new User("白居易", "baijuyi", "123456");
int row = session.insert("user.addUser", user);
System.out.println("影响行数:"+row);
session.commit();
session.close();
}
程序解析:进行数据添加操作时,不要忘记将事务提交,因为MyBatis框架模式是开启事务的,所以要调用SqlSession对象的commit()方法进行提交。
4.5 返回自增长主键
在软件开发时,如果往某张表中插入一条数据记录,而该表的主键又是自增长的设置,那么在执行insert语句时,我们一般不会对主键列做操作,主键列会自动生成并插入到表中。但是主键列的值对于应用来说又是必要的字段,因为可能需要根据主键再去执行相关的修改或者删除操作。如果使用MyBatis框架开发,在插入一条数据时,该如何获得这条记录的自动生成的主键呢?
如果表的主键是自增长设置,那么mysql在执行insert插入提交之前,会自动生成一个自增长主键,并插入到主键列上。获取自增长的主键列的值,可以通过mysql的提供的函数last_insert_id()可以获取到该值,但是此函数的执行必须要在insert语句执行之后才可以,所以在MyBatis中如果想要获取刚插入的子增长主键记录,则在mapper.xml配置文件中,使用如下配置:
mapper.xml配置文件
<!-- 配置insert语句 -->
<insert id="addUser" parameterType="com.langsin.user.pojo.User" >
<selectKey keyProperty="userId" order="AFTER" resultType="int">
select last_insert_id()
</selectKey>
insert into user values(#{user_id}, #{cname}, #{password}, #{username}, #{money})
</insert>
<selectKey>标签用于查询主键,keyProperty属性的作用:是将查询之后得到的主键值设置到parameterType输入映射传入的Java对象的指定属性中。order的作用是指定selectKey元素的查询是在SQL语句之前执行还是在之后执行,可选值为:BEFORE、AFTER,分别表示SQL语句前执行与SQL语句后执行。
注意:这种方式只适用于自增长主键表的插入操作。
测试程序
@Test
public void testAddUser() throws Exception{
User user = new User("王维", "wangwei", "123456");
int row = session.insert("user.addUser", user);
session.commit();
session.close();
System.out.println("影响行数:"+row+",返回自增长主键:"+user.getUser_id());
}
-
4.6 返回非自增长主键
往某张表中插入一条数据,主键是非自增长的varchar类型,生成主键时可以调用mysql数据库的uuid()函数,该函数可以获取一个唯一的随机的字符串,长度为36位,可以利用此函数的值当做表的主键。如果要想使用MyBatis来获取此值,在mapper文件中使用如下配置:
mapper.xml配置文件
<!-- 配置insert语句 -->
<insert id="addUser" parameterType="com.langsin.user.pojo.User" >
<selectKey keyProperty="userId" order="BEFORE" resultType="String">
select uuid()
</selectKey>
insert into user values(#{userId}, #{cname}, #{password}, #{username}, #{money})
</insert>
注意:在上面的配置中执行uuid函数的操作需要在执行insert语句之前,需要先生成主键值再进行插入操作,因此order的值为before。resultType为执行查询之后的返回值类型String。
测试程序同上。
4.7 删除用户操作
在mapper文件中,如果要进行删除操作,就需要使用<delete>标签。在<delete>标签中,同样配置id属性,输入映射parameterType属性。调用的方法为SqlSession对象的delete方法,配置信息如下。
mapper.xml配置文件
<!-- 配置delete删除语句 -->
<delete id="deleteUser" parameterType="int">
delete from user where user_id=#{userId}
</delete>
测试程序
@Test
public void testDeleteUser() throws Exception{
int row = session.delete("user.deleteUser", 12);
session.commit();
System.out.println("影响行数:"+row);
}
-
4.8 更新用户操作
在mapper文件中,如果要进行更新操作,就需要使用<update>标签。在<update>标签中,同样配置id属性,输入映射parameterType属性。调用的方法为SqlSession对象的update方法,配置信息如下。
mapper.xml配置文件
<!-- 配置update更新语句 -->
<update id="updateUser" parameterType="com.langsin.user.pojo.User">
update user set username=#{username},money=#{money} where user_id=#{user_id}
</update>
测试程序
@Test
public void testUpdateUser() throws Exception{
User user = new User("白居易", "xiaobai", "abc123");
user.setMoney(new BigDecimal("2000"));
user.setUser_id(15);
int row = session.update("user.updateUser", user);
session.commit();
System.out.println("影响行数:"+row);
}
-
5.#{}与${}的区别
#{}符号是MyBatis的占位符,相当于jdbc中的“?”,在传参时在参数值外面添加一对单引号。
例如:
select * from user where cname=#{value}
转换后
select * from user where cname='张三'
${}符号是MyBatis的拼接符,对传入的参数不做任何修饰,直接进行简单的字符串拼接。
例如:
select * from user where cname=${value}
转换后
select * from user where cname=张三
-
6.selectOne与selectList
- selectOne方法只能用于返回一条记录,多条记录会抛出异常。
- selectList方法用于返回一个List集合,对于查询结果返回一条记录的也可使用selectList,只不过相当于集合中只有一条记录而已。