环境搭建
mybatis-config.xml配置文件
<?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="db.properties"/>
<typeAliases>
<package name="com.young.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/young/mapper/UserMapper.xml"/>
</mappers>
</configuration>
db.properties配置文件
db.url=jdbc:mysql://localhost:3306/mybatis_db?serverTimezone=UTC
db.username=root
db.password=123
db.driverClassName=com.mysql.jdbc.Driver
UserMapper.xml映射文件
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.young.mapper.UserMapper">
<select id="selectAll" resultType="User">
select * from user
</select>
</mapper>
实体类
public class User {
private Integer id;
private String username;
private String password;
public User() {
}
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
//以下省略了所有成员变量的get和set方法......
}
使用MyBatis的DAO传统开发
编写工具类进行读取配置文件、构建SqlSessionFactory实例操作
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
public class SessionFactoryUtil {
private static Logger log=Logger.getLogger(SessionFactoryUtil.class);
private static SqlSessionFactory sessionFactory;
private static final String CONFIG_FILE_NAME="mybatis-config.xml";
private SessionFactoryUtil() {
}
static{
try {
log.debug("开始加载MyBatis配置文件");
InputStream in = Resources.getResourceAsStream(CONFIG_FILE_NAME);
sessionFactory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
log.error("加载MyBatis配置文件失败");
System.exit(0);
}
}
public static SqlSessionFactory getSessionFactory(){
return sessionFactory;
}
}
DAO层中的接口IUserMapper
import com.young.model.User;
import java.util.List;
public interface IUserMapper {
List<User> selectAll();
void insert(User user);
void update(User user);
void delete(Integer id);
}
UserMapper类
import com.young.model.User;
import com.young.utils.SessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class UserMapper implements IUserMapper {
public List<User> selectAll(){
SqlSession session=null;
try {
session = SessionFactoryUtil.getSessionFactory().openSession();
List<User> list = session.selectList("com.young.mapper.UserMapper.selectAll");
return list;
} finally {
session.close();
}
}
public void insert(User user){
SqlSession session=null;
try {
session = SessionFactoryUtil.getSessionFactory().openSession();
session.insert("com.young.mapper.UserMapper.insert",user);
session.commit();
} finally {
session.close();
}
}
public void update(User user){
SqlSession session=null;
try {
session = SessionFactoryUtil.getSessionFactory().openSession();
session.update("com.young.mapper.UserMapper.update",user);
session.commit();
} finally {
session.close();
}
}
public void delete(Integer id){
SqlSession session=null;
try {
session = SessionFactoryUtil.getSessionFactory().openSession();
session.delete("com.young.mapper.UserMapper.update",id);
session.commit();
} finally {
session.close();
}
}
}
如果使用MyBatis框架这样来编写DAO层中的类,每一个实现类方法中都需要进行try-finally操作,造成代码冗余,且在代码中嵌入了大量的 sqlid。
Mapper代理开发
代码实现
在传统开发开发中,在DAO层中编写接口以及实现类,编写mapper.xml映射文件。而在Mapper代理开发中不需要编写接口的实现类,只需要按照MyBatis的规则对接口和Mapper文件编写,MyBatis会使用代理,自动生成接口的实现类(代理类)。
注:MyBatis框架采用的是JDK代理模式
Mapper代理开发的规则:
1.Mapper.xml映射文件中的namespace必须和接口的全路径保持一致。
2.Mapper.xml映射文件中的方法名必须和接口中的方法名保持一致。
3.因为接口中的方法是可以被重载的,所以Mapper.xml映射文件中Sql-id对应的返回值类型和参数类型需要与接口中保持一致。
4.需要将Mapper接口交给MyBaits框架
UserMapper.xml映射文件
<?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.young.mapper.UserMapper">
<select id="selectAll" resultType="User">
select * from user
</select>
<insert id="insert" parameterType="User">
insert into user values (#{id},#{username},#{password})
</insert>
<update id="update" parameterType="User">
update user set username=#{username} where id=#{id}
</update>
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
</mapper>
UserMapper接口
import com.young.model.User;
import java.util.List;
public interface UserMapper {
List<User> selectAll();
void insert(User user);
void update(User user);
void delete(Integer id);
}
在mybatis-config.xml配置文件中,我们将UserMapper.xml映射文件交给MyBatis框架,现在我们值需要将UserMapper接口交给MyBatis框架。
<!--注意"."和"/"的使用-->
<mappers>
<mapper class="com.young.mapper.UserMapper"/>
</mappers>
测试类
import com.alibaba.fastjson.JSON;
import com.young.mapper.UserMapper;
import com.young.model.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestMapper {
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
//通过getMapper方法获取UserMapper接口的代理类实例
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> list = userMapper.selectAll();
System.out.println(JSON.toJSONString(list,true));
session.close();
}
}
测试其他方法,省略获取创建SqlSession实例和userMapper接口代理类实例的过程。
insert方法
User user = new User(6,"fff","666");
userMapper.insert(user);
session.commit();
session.close();
update方法
User user = new User(2,"efg","345");
userMapper.update(user);
session.commit();
session.close();
delete方法
userMapper.delete(5);
session.commit();
session.close();
获取自增长主键的值
mybatis-config.xml配置文件
<?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="db.properties"/>
<typeAliases>
<package name="com.young.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.young.mapper.UserMapper"/>
</mappers>
</configuration>
前提:数据库支持主键自增长,且主键增加的自增长约束
第一种方式
UserMapper.xml映射文件
<?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.young.mapper.UserMapper">
<!--useGeneratedKeys:表示是否使用自增长主键的值
keyProperty:将主键值与Java对象的属性对应-->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into user(username,password) values (#{username},#{password})
</insert>
</mapper>
测试方法
public class TestMapper {
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("abc");
user.setPassword("123");
userMapper.insert(user);
System.out.println("id-->"+user.getId());
session.commit();
session.close();
}
}
执行插入操作后,数据库会返回插入的数据的主键值并返回给测试方法中User对象的id属性。
第二种方式:
MySQL数据库中提供了内置函数last_insert_id(),用于查询最后一条插入数据的id(主键)的值,只适用于自增长主键。
UserMapper.xml映射文件
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.young.mapper.UserMapper">
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
<!--keyProperty:将主键值与Java对象的属性对应
resultType:返回值的类型
order:执行的顺序,"AFTER"表示在insert语句执行之后执行;"BEFORE"表示在insert语句执行之前执行-->
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into user(username,password) values (#{username},#{password})
</insert>
</mapper>
测试方法
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("abc");
user.setPassword("123");
userMapper.insert(user);
System.out.println("id-->"+user.getId());
session.commit();
session.close();
}
在insert语句执行之后会查询最后一条插入数据的id值,并将结果返回给测试方法中的User类对象的id属性。
控制台输出的日志:
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: insert into user(username,password) values (?,?)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: abc(String), 123(String)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Updates: 1
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert!selectKey] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: select last_insert_id()
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert!selectKey] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters:
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert!selectKey] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Total: 1
通过日志可知,插入语句先执行,"select last_insert_id()"语句后执行。
如果数据库不支持主键自增长,可以使用如下方式(该方式也适用于支持主键自增长的数据库)。
获取UUID作为表中主键值,这样表中主键值就不会重复
uuid()方法返回的是一个字符串,所以需要将主键id的类型更改为varchar类型,否则会报错。在此处以列username为例
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
<selectKey keyProperty="username" resultType="string" order="BEFORE">
select uuid()
</selectKey>
insert into user(username,password) values (#{username},#{password})
</insert>
测试方法
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User();
user.setPassword("123");
userMapper.insert(user);
System.out.println("username-->"+user.getUsername());
session.commit();
session.close();
}
先执行uuid()方法获取一个UUID,然后将该字符串赋值给测试方法中的User类对象的username属性,然后执行插入语句。
Oracle数据库不支持主键自增长,推荐主键的id值为sequence,可以使用函数查询主键值,并设置到属性中(了解即可)。
查询结果无法对应自定义类
如果查询的结果来源于多张表,或者使用了函数,那么结果可能无法与自定义对象对应。如果出现这种情况,那么可以使用Map集合来存储查询到的结果。
UserMapper接口中的方法
Map<String,User> selectById(Integer id);
UserMapper.xml配置文件
<!--因为方法返回值为Map,所以resultType也要设置为Map集合-->
<!--HashMap集合也可以在resultType中直接指定,无需写完整的包名-->
<select id="selectById" resultType="hashmap">
select * from user where id=#{id}
</select>
测试方法
Map<String, User> map = userMapper.selectById(1);
System.out.println(JSON.toJSONString(map,true));
运行结果
查询出的列名作为键
{
"password":"111",
"id":1,
"username":"aaa"
}
注意:当返回结果为Map集合是,只会调用selectOne()方法,如果查询出多条数据,会出现异常。
可以使用@MapKey注解将查询结果的某一列的值作为Map集合的键,值为对象
UserMapper接口中的方法
//如果此处的列名不存在,那么返回的Map集合的键为"null"
@MapKey("id")
Map<String,User> selectById(Integer id);
运行测试方法的运行结果为:
{1:{
"password":"111",
"id":1,
"username":"aaa"
}
}
方法需要传入多个参数
当存在一个方法需要传入多个参数
接口中的方法
User select(Integer id,String username);
UserMapper.xml配置文件
<mapper namespace="com.young.mapper.UserMapper">
<select id="select" resultType="User">
select * from user where id=#{id} and username=#{username}
</select>
</mapper>
测试方法
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.select(1, "aaa");
System.out.println(JSON.toJSONString(user));
session.close();
}
运行测试方法后会出现如下异常
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
所以在传入多个参数时需要用param1、param2…代替属性值
<select id="select" resultType="User">
select * from user where id=#{param1} and username=#{param2}
</select>
如果不想使用param1、param2,也可以使用@Param注解标注方法中的参数
User select(@Param("id")Integer id,@Param("username")String username);
补充:
如果parameterType的类型为Map,即参数是一个Map集合,那么和自定义类传参方式一样,在#{}中写键名。