文章目录
1、自定义DAO接口实现类
CURD操作,即指对数据库实体对象的增(Create)、改(Update)、查(Read)、删(Delete)操作。
1.1 环境搭建
该测试环境是在上一节的基础上进行的,上一节的环境搭建详见《MyBatis使用篇(二)—— MyBatis开发环境搭建》
首先修改“com.ccff.mybatis.dao.IUserDao”,为数据库操作添加接口方法,具体代码修改如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.User;
import java.util.List;
import java.util.Map;
public interface IUserDao {
//插入一个用户
public void insertUser(User user);
//插入一个用户,并获取该用户的id
public void insertUserCatchId(User user);
//根据id删除某个用户
public void deleteUserById(int id);
//修改某个用户
public void updateUser(User user);
//查询所有用户,以List形式返回
public List<User> findAllUserToList();
//查询所有用户,以Map形式返回
public Map<String,User> findAllUserToMap();
//根据id查询某个用户
public User findUserById(int id);
//根据username模糊查询某个用户
public List<User> findUserByName(String username);
}
然后,修改IUserDao的实现类IUserDaoImpl。除去在上一节已经实现的findUserById方法外,对其他方法均空实现,具体代码如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.datasource.DataConnection;
import com.ccff.mybatis.model.User;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class IUserDaoImpl implements IUserDao {
private SqlSession sqlSession;
private DataConnection dataConnection;
public IUserDaoImpl(){
super();
try {
DataConnection dataConnection = new DataConnection();
sqlSession = dataConnection.getSqlSession();
} catch (IOException e) {
e.printStackTrace();
}
}
public SqlSession getSqlSession() {
return sqlSession;
}
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public DataConnection getDataConnection() {
return dataConnection;
}
public void setDataConnection(DataConnection dataConnection) {
this.dataConnection = dataConnection;
}
@Override
public void insertUser(User user) {
}
@Override
public void insertUserCatchId(User user) {
}
@Override
public void deleteUserById(int id) {
}
@Override
public void updateUser(User user) {
}
@Override
public List<User> findAllUserToList() {
return null;
}
@Override
public Map<String, User> findAllUserToMap() {
return null;
}
@Override
public User findUserById(int id) {
User user = sqlSession.selectOne("test.findUserById",id);
this.close();
return user;
}
@Override
public List<User> findUserByName(String username) {
return null;
}
private void close(){
sqlSession.close();
}
}
最后修改MyBatisTest测试类,具体代码如下:
package com.ccff.mybatis.test;
import com.ccff.mybatis.dao.IUserDao;
import com.ccff.mybatis.model.User;
import com.ccff.mybatis.dao.IUserDaoImpl;
import org.junit.Test;
import java.text.SimpleDateFormat;
public class MyBatisTest {
private IUserDao userDao;
public MyBatisTest(){
super();
userDao = new IUserDaoImpl();
}
@Test
public void TestSelect() {
User user = userDao.findUserById(1);
System.out.println("姓名:"+user.getUsername());
System.out.println("性别:"+user.getGender());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd");
System.out.println("生日:"+simpleDateFormat.format(user.getBirthday()));
System.out.println("所在地:"+user.getProvince());
}
}
至此,开发测试基础环境搭建完毕。
1.2 插入一个用户
第一步,修改SQL语句映射文件Mapper.xml,添加insert标签插入语句,修改后具体代码如下所示:
<?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="test">
<insert id="insertUser" parameterType="com.ccff.mybatis.model.User">
insert into user (username,password,gender,email,province,city,birthday) values (#{username},#{password},#{gender},#{email},#{province},#{city},#{birthday})
</insert>
<select id="findUserById" parameterType="int" resultType="com.ccff.mybatis.model.User">
select * from user where id=#{id}
</select>
</mapper>
第二步,修改IUserDao的实现类IUserDaoImpl,填充insertUser方法,具体代码如下:
@Override
public void insertUser(User user) {
sqlSession.insert("test.insertUser",user);
sqlSession.commit();
this.close();
}
第三步,修改MyBatisTest,测试insertUser方法,具体代码如下:
package com.ccff.mybatis.test;
import com.ccff.mybatis.dao.IUserDao;
import com.ccff.mybatis.model.User;
import com.ccff.mybatis.dao.IUserDaoImpl;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
private IUserDao userDao;
public MyBatisTest(){
super();
userDao = new IUserDaoImpl();
}
@Test
public void TestInsertUser(){
Date day = new Date();
User user = new User("王五","123456","男","[email protected]","辽宁省","沈阳市",day);
userDao.insertUser(user);
}
}
第四步,运行MyBatisTest后,查看日志信息如下:
第五步,进入数据库后查看user表,发现刚刚插入的记录已经存在,说明插入执行成功。具体如下所示:
1.3 插入用户后用新id初始化插入对象
回顾1.2小节中演示的插入用户信息的实例,我们发现其实我们在new一个插入的用户对象实例的时候并没有指定该用户实例对象的id值,但是当我们执行插入数据库操作时却可以得到一个自增的id值。
那么,现在有这样一个问题值得我们思考:如何获取创建的插入用户实例对象的自增id呢?
在MySQL中,在插入语句后执行如下语句,则会输出新插入记录的id。
select @@identity;
#或者
select last_insert_id();
因此,在SQL映射文件的insert标签中,有一个名为“selectKey”的子标签用于获取新插入记录的主键值。以下两种写法均可以完成“使用新插入记录的主键值初始化被插入的对象”的功能。
<insert id="insertUser" parameterType="com.ccff.mybatis.model.User">
insert into user (username,password,gender,email,province,city,birthday) values (#{username},#{password},#{gender},#{email},#{province},#{city},#{birthday})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@identity
</selectKey>
</insert>
或
<insert id="insertUserCatchId" parameterType="com.ccff.mybatis.model.User">
insert into user (username,password,gender,email,province,city,birthday) values (#{username},#{password},#{gender},#{email},#{province},#{city},#{birthday})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select last_insert_id();
</selectKey>
</insert>
其中,resultType指出获取的主键的类型;keyProperty指出主键在Java实体类中对应的属性名。此处会将获取的主键值直接封装到被插入的User对象中;order指出id的生成相对于insert语句的执行是在前还是在后。MySQL数据库表中的id,均是先执行insert语句,而后生成id,所以需要设置为AFTER。Oracle数据库表中的id则是在insert执行之前生成,所以需要设置为BEFORE。当前的MyBatis版本,不指定order属性,则会根据所用DBMS自动选择其值。
接下来修改IUserDaoImpl,为其中的insertUserCatchId方法添加具体方法体。具体方法同之前的插入方法,代码如下:
@Override
public void insertUserCatchId(User user) {
sqlSession.insert("test.insertUserCatchId",user);
sqlSession.commit();
this.close();
}
然后修改测试类MyBatisTest,添加测试方法TestInsertUserCatchId,具体代码如下:
@Test
public void TestInsertUserCatchId(){
Date day = new Date();
User user = new User("王五","123456","男","[email protected]","辽宁省","沈阳市",day);
System.out.println("插入前User = "+user);
userDao.insertUserCatchId(user);
System.out.println("插入后User = "+user);
}
最后,执行测试代码后数据表中存在刚刚插入的数据,并且从控制台打印的日志中可以看到,插入前插入user对象的id为默认值0,而插入后user对象的id为刚刚插入数据库后数据表中自增id值,说明测试通过。
1.4 根据id删除某个用户
第一步,修改SQL映射文件,添加delete标签和删除语句,具体如下。这里需要注意的是,这里的参数类型为int类型(基本数据类型),因此后面#{}里面的值可以是任意字符,仅起到占位符的作用。
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
第二步,修改IUserDaoImpl实现类,填充deleteUserById空方法,具体代码如下:
@Override
public void deleteUserById(int id) {
sqlSession.delete("test.deleteUserById",id);
sqlSession.commit();
this.close();
}
第三步,修改MyBatisTest测试类,在该类中添加测试方法TestDeleteUserById,具体代码如下所示:
@Test
public void TestDeleteUserById(){
userDao.deleteUserById(10);
}
第四步,查看当前user数据表如下所示:
第五步,运行TestDeleteUserById测试方法后,查看user数据表中,发现id为10的记录已经被删除,说明测试通过。
1.5 修改某个用户
第一步,修改SQL映射文件,添加update标签和更新SQL语句。这里需要注意的是:由于参数为包装类型,因此这里的#{}里面的内容必须与User对象的属性名称一致,不能再随意写,具体代码如下:
<update id="updateUser" parameterType="com.ccff.mybatis.model.User">
update user set username=#{username} where id=#{id}
</update>
第二步,修改IUserDaoImpl实现类,填充updateUser空方法,具体代码如下:
@Override
public void updateUser(User user) {
sqlSession.update("test.updateUser",user);
sqlSession.commit();
this.close();
}
第三步,修改MyBatisTest测试类,在该类中添加测试方法TestUpdateUser,具体代码如下所示:
@Test
public void TestUpdateUser(){
User user = new User();
user.setId(4);
user.setUsername("王丽");
userDao.updateUser(user);
}
第四步,查看当前user数据表如下所示:
第五步,运行TestUpdateUser测试方法后,查看user数据表中,发现id为4的记录中username由原来的“孙丽”变为了现在的“王丽”,说明测试通过。
1.6 查询所有用户,以List形式返回
第一步,修改SQL映射文件,添加id为“findAllUserToList”的select标签。注意,resultType属性并非指查询结果集最后的类型,而是每查出DB中的一条记录,将该记录封装成为的对象的类型。这里的resultType属性使用的是全限定性类名。
<select id="findAllUserToList" resultType="com.ccff.mybatis.model.User">
select * from user
</select>
第二步,修改IUserDaoImpl实现类,填充findAllUserToList空方法,具体代码如下:
@Override
public List<User> findAllUserToList() {
List<User> users = null;
users = sqlSession.selectList("test.findAllUserToList");
this.close();
return users;
}
第三步,修改MyBatisTest测试类,在该类中添加测试方法TestFindAllUserToList,具体代码如下所示:
@Test
public void TestFindAllUserToList(){
List<User> users = userDao.findAllUserToList();
for (User user : users){
System.out.println(user);
}
}
第四步,查看当前user数据表如下所示:
第五步,运行TestFindAllUserToList测试方法后,得到如下日志信息,且在控制台输出了全部用户信息,说明测试通过。
1.7 查询所有用户,以Map形式返回
第一步,修改SQL映射文件,添加id为“findAllUserToMap”的select标签。
<select id="findAllUserToMap" resultType="com.ccff.mybatis.model.User">
select * from user
</select>
第二步,修改IUserDaoImpl实现类,填充findAllUserToMap空方法,这里使用SqlSession的selectMap方法完成查询操作。该查询方法会将查询出的每条记录先封装成指定对象,然后再将该对象作为value,将该对象的指定属性所对应的字段名作为key封装为一个Map对象。具体代码如下:
@Override
public Map<String, User> findAllUserToMap() {
Map<String,User> userMap = null;
userMap = sqlSession.selectMap("test.findAllUserToMap","username");
this.close();
return userMap;
}
第三步,修改MyBatisTest测试类,在该类中添加测试方法TestFindAllUserToMap,具体代码如下所示:
@Test
public void TestFindAllUserToMap(){
Map<String,User> userMap = userDao.findAllUserToMap();
User user = userMap.get("张三");
System.out.println(user);
}
第四步,查看当前user数据表如下所示:
第五步,运行TestFindAllUserToMap测试方法后,得到如下日志信息,且在控制台输出了username为“张三”的用户信息,说明测试通过。
1.8 根据username模糊查询某个用户
第一步,修改SQL映射文件,添加id为“findUserByName”的select标签。具体代码如下:
<select id="findUserByName" parameterType="String" resultType="com.ccff.mybatis.model.User">
select * from user where username like '%' #{username} '%'
</select>
这种方式是以动态参数的形式出现在SQL语句中的。在进行模糊查询时,需要进行字符串的拼接,SQL语句中的字符串拼接使用的是函数concat(arg1,arg2,…)。注意,不能使用Java中的字符串连接符+。上面的SQL语句写法与下面的写法是等效的。
<select id="findUserByName" parameterType="String" resultType="com.ccff.mybatis.model.User">
select * from user where username like concat('%',#{username},'%')
</select>
除此之外,还可以使用如下方式,但是需要注意,下面的${}中只能使用value,不能使用其他。而且这种方式是纯粹的字符串拼接,直接将参数拼接到了SQL语句中,也就是说这种方式可能会发生SQL注入问题。
<select id="findUserByName" parameterType="String" resultType="com.ccff.mybatis.model.User">
select * from user where username like '%'${value}'%'
</select>
第二步,修改IUserDaoImpl实现类,填充findUserByName空方法,具体代码如下:
@Override
public List<User> findUserByName(String username) {
List<User> users = null;
users = sqlSession.selectList("test.findUserByName",username);
this.close();
return users;
}
第三步,修改MyBatisTest测试类,在该类中添加测试方法TestFindUserByName,具体代码如下所示:
@Test
public void TestFindUserByName(){
List<User> users = userDao.findUserByName("丽");
for (User user : users){
System.out.println(user);
}
}
第四步,查看当前user数据表如下所示:
第五步,运行TestFindUserByName测试方法后,得到如下日志信息,且在控制台输出了username中包含“丽”的用户信息,说明测试通过。
1.9 parameterType的别名问题
对于一个映射文件来说,一般情况下是对一个类的操作均放在同一个映射文件。所以,一个映射文件中出现的类一般都是相同的,而每一个需要指定类名的地方均需要全限定性类名,会比较麻烦。所以,MyBatis支持为类起别名的方式。
指定别名的第一种方式: 在全局配置文件中properties标签和setting标签后,添加typeAliases标签,指定类的别名。其中,type表示全限定性类名,alias表示别名。该方式的好处是可以指定别名为简单类型以外的其他形式。当然,弊端就是必须为每一个类逐个指定,比较繁琐。具体如下:
<?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" />
<settings>
<setting name="logImpl" value="LOG4J" />
</settings>
<typeAliases>
<typeAlias type="com.ccff.mybatis.model.User" alias="User"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<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="sqlmap/UserMapper.xml"/>
</mappers>
</configuration>
指定别名的第一种方式: 对于实体类的全限定性类名的别名指定方式,一般采用package标签方式配置。这样的好处是会将该包中所有实体类的简单类名指定为别名,写法简单方便。但是弊端是只能将类的简单类名作为别名。具体配置如下:
<?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" />
<settings>
<setting name="logImpl" value="LOG4J" />
</settings>
<typeAliases>
<!--<typeAlias type="com.ccff.mybatis.model.User" alias="User"/>-->
<package name="com.ccff.mybatis.model"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<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="sqlmap/UserMapper.xml"/>
</mappers>
</configuration>
当采用第二种方式配置了别名后,之前的SQL映射文件中的全限定性类名就可以用其简单类名代替,具体修改为如下所示:
<?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="test">
<insert id="insertUser" parameterType="User">
insert into user (username,password,gender,email,province,city,birthday) values (#{username},#{password},#{gender},#{email},#{province},#{city},#{birthday})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@identity
</selectKey>
</insert>
<insert id="insertUserCatchId" parameterType="User">
insert into user (username,password,gender,email,province,city,birthday) values (#{username},#{password},#{gender},#{email},#{province},#{city},#{birthday})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@identity
</selectKey>
</insert>
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
<update id="updateUser" parameterType="User">
update user set username=#{username} where id=#{id}
</update>
<select id="findAllUserToList" resultType="User">
select * from user
</select>
<select id="findAllUserToMap" resultType="User">
select * from user
</select>
<select id="findUserById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
<select id="findUserByName" parameterType="String" resultType="User">
select * from user where username like '%' #{username} '%'
</select>
</mapper>
2、属性名与查询字段名不同
resultType可以将查询结果直接映射为实体Bean对象的条件是:SQL查询的字段名与实体Bean的属性名已知。 因为在将查询结果转换为指定类型对象时,系统自动将查询结果字段名称作为对象的属性名,通过反射机制完成对象的创建。
当SQL查询结果的字段名与实体Bean的属性名不一致时,将无法创建出需要类型的对象,此时有两种解决方法。
2.1 测试环境搭建
对原有的user表中的字段名进行修改,将原有的username字段名修改为name。修改后的表结构如下图所示:
2.2 查询字段使用别名
虽然属性名称与表中字段名称不一致,但可以为查询结果的字段名赋予别名,让别名与实体Bean的属性名相同。这样框架也可以根据查询结果利用反射机制将对象创建。
假设当前需要查询id为3的用户的用户名。第一步,在SQL映射文件中修改相应的根据id查询用户信息的SQL语句,修改如下:
<select id="findUserByIdAlias" parameterType="int" resultType="User">
select id,name username,password,gender,email,province,city,birthday from user where id=#{id}
</select>
UserDao实现类IUserDaoImpl中的具体代码如下:
@Override
public User findUserById(int id) {
User user = sqlSession.selectOne("test.findUserByIdAlias",id);
this.close();
return user;
}
测试类MyBatisTest中测试该查询的方法TestSelectByIdAlias代码如下:
@Test
public void TestSelectByIdAlias() {
User user = userDao.findUserById(3);
System.out.println(user);
}
运行测试方法TestSelectByIdAlias后,在控制台输出id为3的用户信息,说明测试通过。
2.3 使用结果映射resultMap
可以使用结果映射resultMap(这里的Map是映射mapper的意思)来建立映射关系,完成由字段到属性的映射,达到将查询结果封装为对象的目的。 resultMap是对resultType的增强。
修改SQL映射文件,添加resultMap映射配置如下:
<resultMap id="userMapper" type="User">
<result column="name" property="username"/>
</resultMap>
<select id="findUserByIdAlias" parameterType="int" resultType="User" resultMap="userMapper">
select * from user where id=#{id}
</select>
其余的配置不动,再次运行测试方法TestSelectByIdAlias后,在控制台输出id为3的用户信息,说明测试通过。
这里需要注意的是:在resultMap标签中,定义了由type指定的类的属性名到表中字段名称的映射关系。根据这个映射关系,框架利用反射机制创建相应的对象。
- type:指定要映射的实体类
- id:指定该resultMap映射关系的名称
- < id >标签:id的字段名column与实体类的属性property间的映射关系
- < result >标签:id以外其他字段名column与实体类的属性property间的映射关系。
这里可以看到,对于自增id字段的映射与普通字段的映射是由区别的。对于字段名与实体类属性名相同的情况,可以不写入resultMap标签中。