1. MyBatis框架的作用
MyBatis框架是一个持久层框架,用于解决数据库持久化存储方案。
在Java语言中,可以通过JDBC技术实现数据库编程,但是,JDBC技术最大的问题就是编程套路固定,需要编写的代码大致相同,所以就产生了多种解决此问题的框架,例如Hibernate、MyBatis等,这些框架都可以大大的减少开发的工作量,甚至在某些细节的处理上,比手动编写JDBC代码处理得更好!
MyBatis就是可以明显简化开发的数据库持久化存储方案,开发者在使用MyBatis开发某个增/删/改/查功能时,只需要定义这个功能的抽象方法,及匹配的SQL语句即可!
除此以外,MyBatis还提供了数据缓存的处理等其它功能!
2. 创建项目
使用创建SpringMVC项目的步骤创建新的项目,Group Id使用cn.tedu,Artifact Id使用MyBatis,Packaing选择war(其实单机的MyBatis项目并不需要使用war,而只使用jar即可)。
关于MyBatis项目,必须添加的依赖有:
<!-- MyBatis框架 -->
<!-- 备选版本:3.5.0 ~ 3.5.3 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- MyBatis整合Spring框架 -->
<!-- 备选版本:2.0.0 ~ 2.0.3 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<!-- Spring-WEBMVC -->
<!-- 版本:不低于4.2即可 -->
<!-- 只使用MyBatis的话,其实,只需要spring-context即可,spring-webmvc中包含了spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>XXX</version>
</dependency>
<!-- Spring-JDBC -->
<!-- 版本:必须与使用的spring-webmvc保持一致 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>XXX</version>
</dependency>
<!-- MySQL Connector -->
<!-- 备选版本:8.0.12 ~ 8.0.18 -->
<!-- 备选版本:5.1.34 ~ 5.1.39 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- 数据库连接池 -->
<!-- 备选版本:1.2, 1.2.2, 1.3, 1.4 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- 单元测试 -->
<!-- 备选版本:4.10, 4.11, 4.12 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
3. 连接数据库
首先,应该在src/main/resources下创建数据库连接相关配置的db.properties文件:
url=jdbc:mysql://localhost:3306/tedu_ums?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Chongqing
driver=com.mysql.cj.jdbc.Driver
username=root
password=root
initialSize=2
maxActive=10
将现有的Spring配置文件改名为spring-mvc.xml,并将此文件复制得到spring-dao.xml文件,在当前项目,后续就只使用spring-dao.xml文件即可!
在spring-dao.xml中,添加配置,读取以上db.properties,并将读到的值注入至BasicDataSource对象中:
<util:properties id="config"
location="classpath:db.properties" />
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="#{config.url}" />
<property name="driverClassName" value="#{config.driver}" />
<property name="username" value="#{config.username}" />
<property name="password" value="#{config.password}" />
<property name="initialSize" value="#{config.initialSize}" />
<property name="maxActive" value="#{config.maxActive}" />
</bean>
完成后,在src/main/test下创建cn.tedu.mybatis.Tests单元测试类,在类中测试获取数据库连接对象:
public class Tests {
ClassPathXmlApplicationContext ac;
@Before
public void doBefore() {
ac = new ClassPathXmlApplicationContext(
"spring-dao.xml");
}
@Test
public void getConnection() throws SQLException {
BasicDataSource dataSource
= ac.getBean("dataSource", BasicDataSource.class);
System.out.println(dataSource.getConnection());
}
@After
public void doAfter() {
ac.close();
}
}
4. 接口与抽象方法
暂定当前目标:向数据库中插入1条用户数据。
MyBatis要求所有的抽象方法都应该定义在接口中,所以,先创建
cn.tedu.mybatis.UserMapper接口:
public interface UserMapper {
}
然后,需要在接口中定义“增加用户数据”的抽象方法,关于抽象方法的设计原则:
如果需要执行的数据操作是增、删、改类型的操作,应该将返回值类型声明为Integer,表示受影响的行数,如果不关心受影响的行数,也可以将返回值类型声明为void,但是,并不推荐这样做;如果需要执行的数据操作是查询操作,返回值类型可以设计为所期望的类型,只要能把查询结果都封装起来就可以;
方法名称可以自由定义,但是,不允许使用重载;
参数列表可以根据需要执行的SQL语句有哪些变量来决定,也可以参考以前学习JDBC时,SQL语句中需要写哪些问号来决定。
此次插入用户数据需要执行的SQL语句大致是:
insert into t_user (username,password,age,phone,email) values (?,?,?,?,?)
为了更加方便表达参数,可以将用户数据封装起来,所以,先创建cn.tedu.mybatis.User类,在类中声明与数据表字段对应的属性:
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String phone;
private String email;
// SET/GET/toString
}
则在接口中添加抽象方法:
Integer insert(User user);
完成以上代码后,目前,框架并不知道在哪个包中有哪些接口需要处理,所以,需要在spring-dao.xml添加配置MapperScannerConfigurer的basePackage属性:
<!-- MapperScannerConfigurer -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 接口文件在哪里 -->
<property name="basePackage"
value="cn.tedu.mybatis" />
</bean>
至此,关于接口与抽象方法部分的开发已经完成,但是,并不能执行任何测试检测开发与配置的效果!
5. 配置SQL语句
先下载http://doc.tedu.cn/config/mybatis-mapper.zip文件,解压得到SomeMapper.xml文件。
在项目的src/main/resources下创建名为mappers的文件夹,并将以上得到的XML文件复制到该文件夹中。
与此前的抽象方法对应的SQL语句都需要配置在这个XML文件中!
在XML中,首先需要添加根节点,并且,在该节点中配置namespace的属性,该属性值是需要对应的接口的全名:
<!-- namespace:对应哪个接口 -->
<mapper namespace="cn.tedu.mybatis.UserMapper">
</mapper>
然后,根据需要执行的SQL语句的种类,选择子级节点,以本次插入用户数据为例,则应该选择节点,这些节点必须指定id属性,其值是抽象方法的名称:
<!-- id:抽象方法的名称 -->
<insert id="insert">
</insert>
在该节点内部,配置需要执行的SQL语句:
<!-- id:抽象方法的名称 -->
<insert id="insert">
INSERT INTO t_user (
username, password,
age, phone,
email
) VALUES (
#{username}, #{password},
#{age}, #{phone},
#{email}
)
</insert>
以上代码中,使用#{}框住的名称,其实是User类中的属性名!
完成后,还需要配置SqlSessionFactoryBean,配置框架执行时使用的数据源,及配置SQL语句的XML文件的位置:
<!-- SqlSessionFactoryBean -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- XML文件在哪里 -->
<property name="mapperLocations"
value="classpath:mappers/*.xml" />
<!-- 使用哪个数据源 -->
<property name="dataSource"
ref="dataSource" />
</bean>
至此,开发任务完成,在Tests测试类中编写并执行单元测试:
@Test
public void insert() {
// 获取UserMapper接口的代理对象
UserMapper userMapper
= ac.getBean("userMapper", UserMapper.class);
// 测试功能
User user = new User();
user.setUsername("mapper");
user.setPassword("8888");
user.setAge(25);
user.setPhone("13800139999");
user.setEmail("mapper@163.com");
Integer rows = userMapper.insert(user);
System.out.println("rows=" + rows);
}
如果执行出错,并且,不是注册重复的用户名,则错误的原因可能在于:
配置的接口文件的位置有误,导致无法获取到名为userMapper的对象;
配置的XML文件的位置有误;
在XML文件中,根节点的namespace属性值有误;
在节点中,id属性的值并不是抽象方法的名称;
6. 开发新功能
6.1. 根据id删除指定的数据
在UserMapper接口中声明抽象方法:
Integer deleteById(Integer id);
在SomeMapper.xml中配置以上抽象方法的映射:
<delete id="deleteById">
DELETE FROM t_user WHERE id=#{id}
</delete>
完成后,在Tests中编写并执行单元测试:
@Test
public void deleteById() {
Integer id = 1;
Integer rows = userMapper.deleteById(id);
System.out.println("rows=" + rows);
}
6.2. 将所有用户的密码全部改为888888
在UserMapper接口中声明抽象方法:
Integer updatePassword(String newPassword);
在SomeMapper.xml中配置以上抽象方法的映射:
<update id="updatePassword">
UPDATE t_user SET password=#{newPassword}
</update>
完成后,在Tests中编写并执行单元测试:
@Test
public void updatePassword() {
String newPassword = "88888888";
Integer rows = userMapper.updatePassword(newPassword);
System.out.println("rows=" + rows);
}
6.3. 统计当前表中的用户数据的数量
在UserMapper接口中声明抽象方法:
Integer count();
在SomeMapper.xml中配置以上抽象方法的映射:
<select id="count" resultType="java.lang.Integer">
SELECT COUNT(*) FROM t_user
</select>
注意:使用<select>节点时,必须配置resultType或resultMap属性中的某1个!
完成后,在Tests中编写并执行单元测试:
@Test
public void count() {
Integer count = userMapper.count();
System.out.println("count=" + count);
}
6.4. 查询指定id的数据
在UserMapper接口中声明抽象方法:
User findById(Integer id);
在SomeMapper.xml中配置以上抽象方法的映射:
<select id="findById" resultType="cn.tedu.mybatis.User">
SELECT * FROM t_user WHERE id=#{id}
</select>
完成后,在Tests中编写并执行单元测试:
@Test
public void findById() {
Integer id = 1;
User user = userMapper.findById(id);
System.out.println(user);
}
在测试时,如果存在与id匹配的数据,则返回匹配的数据,如果没有匹配的数据,则返回结果是null。
6.5. 查询当前表中所有用户的所有数据
在UserMapper接口中声明抽象方法:
List<User> findAll();
在SomeMapper.xml中配置以上抽象方法的映射:
<select id="findAll" resultType="cn.tedu.mybatis.User">
SELECT * FROM t_user ORDER BY id
</select>
注意:如果查询结果是List类型的,在配置时,resultType也只需要声明List中的元素的类型!
完成后,在Tests中编写并执行单元测试:
@Test
public void findAll() {
List<User> users = userMapper.findAll();
System.out.println("count=" + users.size());
for (User user : users) {
System.out.println(user);
}
}
如果表中并没有匹配的数据,查询结果是List类型时,也不会返回null,而是返回一个没有元素的List对象!
6.6. 将id=?的用户的电子邮箱改为?
在UserMapper接口中声明抽象方法:
Integer updateEmailById(
@Param("id") Integer id,
@Param("email") String email);
注意:当抽象方法的参数的数量超过1个时,必须为每一个参数添加@Param注解,并在注解中指定名称,后续,在配置SQL语句时,在#{}中写的也将是注解中配置的名称!
在SomeMapper.xml中配置以上抽象方法的映射:
<update id="updateEmailById">
UPDATE t_user SET email=#{email} WHERE id=#{id}
</update>
完成后,在Tests中编写并执行单元测试:
@Test
public void updateEmailById() {
Integer id = 1;
String email = "hello@163.com";
Integer rows = userMapper.updateEmailById(id, email);
System.out.println("rows=" + rows);
}