MyBatis
MyBatis是一个优秀的持久化框架,它对JDBC的操作数据库的过程进行封装,使开发者只关注SQL本身,而不需要花费精力去处理注册驱动、创建connection、创建statement、手动设置参数和结果集检索等JDBC繁杂的过程代码。
MyBatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由MyBatis框架执行sql,并将结果映射成java对象进行返回。
首先我们来创建一个mybatis_db的数据库:
set foreign_key_checks=0;
-- ----------------------------
-- table structure for `orders`
-- ----------------------------
drop table if exists `orders`;
create table `orders` (
`id` int(11) not null auto_increment,
`user_id` int(11) not null comment '下单用户id',
`number` varchar(32) not null comment '订单号',
`createtime` datetime not null comment '创建订单时间',
`note` varchar(100) default null comment '备注',
primary key (`id`),
key `fk_orders_1` (`user_id`),
constraint `fk_orders_id` foreign key (`user_id`) references `user` (`id`) on delete no action on update no action
) engine=innodb auto_increment=6 default charset=utf8;
-- ----------------------------
-- records of orders
-- ----------------------------
insert into `orders` values ('3', '1', '1000010', '2015-02-04 13:22:35', null);
insert into `orders` values ('4', '1', '1000011', '2015-02-03 13:22:41', null);
insert into `orders` values ('5', '10', '1000012', '2015-02-12 16:13:23', null);
-- ----------------------------
-- table structure for `user`
-- ----------------------------
drop table if exists `user`;
create table `user` (
`id` int(11) not null auto_increment,
`username` varchar(32) not null comment '用户名称',
`birthday` date default null comment '生日',
`sex` char(1) default null comment '性别',
`address` varchar(256) default null comment '地址',
primary key (`id`)
) engine=innodb auto_increment=27 default charset=utf8;
-- ----------------------------
-- records of user
-- ----------------------------
insert into `user` values ('1', '王五', null, '2', null);
insert into `user` values ('10', '张三', '2014-07-10', '1', '北京市');
insert into `user` values ('16', '张小明', null, '1', '河南郑州');
insert into `user` values ('22', '陈小明', null, '1', '河南郑州');
insert into `user` values ('24', '张三丰', null, '1', '河南郑州');
insert into `user` values ('25', '陈小明', null, '1', '河南郑州');
insert into `user` values ('26', '王五', null, null, null);
JDBC
使用JDBC操作数据库使用如下代码进行:
public class JdbcDemo {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_db?characterEncoding=utf-8", "root", "123456");
// 定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
// 获取预处理statement
preparedStatement = connection.prepareStatement(sql);
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
// 向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
从上面的操作可以看出JDBC所存在的问题:
1、频繁创建数据库连接和释放导致资源浪费,从而影响性能,可以使用数据库连接池解决。
2、sql语句在代码中是硬编码,造成不易维护,经常性需要修改代码。
3、占位符的位置是硬编码,一旦改变位置就容易导致错误。
4、对结果集解析存在硬编码,sql变化则导致解析代码变化,系统不易维护。
MyBatis
MyBatis架构
Mybatis架构图如下所示:
- Mybatis介绍
Mybatis主要包含配置文件和映射文件,分别如下所示:
SqlMapConfig.xml:此文件作为mybatis的全局配置文件,配置了Mybatis的运行环境等信息。
mapper.xml文件是sql映射文件,文件中配置操作数据库的sql语句,它需要在sqlMapConfig.xml中加载。
通过Mybatis环境等配置信息构造SqlSessionFactory会话工程。由会话工厂创建SqlSession会话,操作数据库由SqlSession进行。Mybatis底层自定义Executor执行器接口操作数据库,该接口有两个实现,基本执行器和缓存执行器。Mapped Statement也是Mybatis底层封装对象,它包装Mybatis配置信息及sql映射信息等。
-
Mybatis配置
SqlMapConfig.xml中配置的内容和顺序如下:
标签 | 描述 |
---|---|
properties | 属性,配置在properties中的属性可以通过表达式的方式引用。 |
settings | 全局配置参数 |
typeAliases | 类型别名 |
typeHandlers | 类型处理器 |
objectFactory | 对象工厂 |
plugins | 插件 |
evironments | 环境集合属性对象,它包含如下子集: environment:环境子属性对象 transactionManager:事务管理 dataSource:数据源 |
mappers | 映射器 |
-
properties
SqlMapConfig.xml可以引用java属性文件中的配置信息:
在resources下定义db.properties文件,如下所示:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db?characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
SqlMapConfig.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"/>
<!-- 和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>
<!--Mapper的位置-->
<mappers>
<!--resource目录下的全路径,这样则可以不用编写dao层实现类-->
<mapper resource="sqlmap/User.xml"></mapper>
</mappers>
</configuration>
注意: MyBatis 将按照下面的顺序来加载属性:
在 properties元素体内定义的属性首先被读取。
然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
-
自定义类型别名
首先在SqlMapConfig.xml中配置自定义类型别名
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="com.legend.mybatis.pojo.User" />
<!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
<package name="com.legend.mybatis.pojo" />
<package name="其它包" />
</typeAliases>
在mapper.xml配置文件中,就可以使用设置的别名,别名大小写不敏感。也可以写成USER。
<?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.legend.mybatis.mapper.UserMapper">
<insert id="insertUser" parameterType="user">
insert into user (username, birthday, address, sex) values (#{username}, #{birthday}, #{address}, #{sex})
</insert>
</mapper>
当然我们还可以引入某一个包下的所有类:
<typeAliases>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
<package name="com.legend.mybatis.pojo"/>
</typeAliases>
-
mappers
Mapper配置的几种方法:
1、使用相对于类路径的资源(现在的使用方式)
<mapper resource="sqlmap/User.xml" />
2、使用mapper接口类路径,此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<mapper class="com.legend.mybatis.mapper.UserMapper"/>
3、注册指定包下的所有mapper接口,此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name="com.legend.mybatis.mapper"/>
MyBatis使用
使用MyBatis之前需要引入它的核心包,可以使用maven直接构建:
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
- 开发步骤
1、根据创建的数据库表来简历pojo映射表名的序列化类:
public class User implements Serializable {
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
...省略Get和Set方法...
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address + "]";
}
}
2、在resources目录下创建sqlMapConfig.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"/>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!--Mapper的位置-->
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
如果在没有使用Spring的情况下则需要配置enviroments标签中的内容来配置连接信息,如果有Spring的话则不需要配置。
3、在resources目录下创建log4j.properties用来在控制台打印日志信息
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# 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
4、在resources下创建sqlmap文件夹,如果要映射User表,则在sqlmap下创建User.xml文件,用来编写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">
<!-- 写Sql语句 -->
<mapper namespace="test">
<!--通过id查询用户
id:唯一标识符,和namespace配合使用
parameterType:占位符的类型
resultType:返回值的类型,必须指定全包名
-->
<select id="findUserById" parameterType="Integer" resultType="com.legend.mybatis.pojo.User">
select * from user where id = #{value}
</select>
</mapper>
5、编写测试类进行测试
public class Demo {
// 通过id查询用户
@Test
public void findByID() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、执行Sql语句
User user = sqlSession.selectOne("test.findUserById", 10);
System.out.println(user);
}
}
增删改查(CRUD)
-
添加用户
Mybatis不但可以直接添加用户,还可以在添加用户后立刻返回该用户的id值,其中<selectKey>标签就表示返回值的配置。
<?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.legend.mybatis.pojo.User">
<selectKey keyProperty="id" resultType="Integer" order="AFTER">
<!--保存用户后直接返回最新id值-->
select LAST_INSERT_ID()
</selectKey>
insert into user (username, birthday, address, sex) values (#{username}, #{birthday}, #{address}, #{sex})
</insert>
</mapper>
测试代码如下:
@Test
public void insertUser() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、执行Sql语句
User user = new User();
user.setUsername("陈冠希");
user.setBirthday(new Date());
user.setAddress("香港");
user.setSex("男");
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
System.out.println(user.getId());
}
-
删除用户
通过id来删除用户比较简单,下面的删除的操作
<?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">
<!--删除用户-->
<delete id="deleteUserById" parameterType="Integer">
delete from user where id = #{value}
</delete>
</mapper>
测试代码如下:
@Test
public void deleteUser() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、执行Sql语句
sqlSession.delete("test.deleteUserById", 30);
sqlSession.commit();
}
-
更新用户
通过id进行更新用户的时候,在测试代码中传入给User.xml中的是User对象,因为修改用户信息的操作是在User.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="test">
<!--更新用户-->
<update id="updateUserById" parameterType="com.legend.mybatis.pojo.User">
update user set username=#{username}, sex = #{sex}, birthday=#{birthday}, address=#{address}
where id = #{id}
</update>
</mapper>
测试代码如下:
@Test
public void updateUser() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、执行Sql语句
User user = new User();
user.setId(30);
user.setUsername("周杰伦");
user.setBirthday(new Date());
user.setAddress("台湾");
user.setSex("男");
int update = sqlSession.update("test.updateUserById", user);
sqlSession.commit();
}
-
查找用户
由于查找用户在上方已经演示过,这里演示通过用户名模糊查询用户列表
<?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">
<select id="findByUsername" parameterType="String" resultType="com.legend.mybatis.pojo.User">
select * from user where username like '%${value}%'
</select>
</mapper>
注意:#{}的形式表示的是占位符;({}表示的是字符串的拼接,并且必须是){value}的格式,否则会抛出异常信息。
测试代码如下:
// 通过用户名模糊查询用户列表
@Test
public void findByUserName() throws Exception{
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、执行Sql语句
List<User> list = sqlSession.selectList("test.findByUsername", "五");
for (User user : list) {
System.out.println(user);
}
}
Mapper动态代理
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法一样。
Mapper接口开发需要遵循如下规范:
1、Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
Mapper使用
1、创建名称为UserMapper的接口,并定义增删改查的方法。
public interface UserMapper {
// 添加用户
void insertUser(User user);
// 删除用户
void deleteUserById(Integer id);
// 修改用户
void updateUserById(User user);
// 查找用户
User findUserById(Integer id);
}
2、创建Mapper.xml配置文件,在resources/sqlmap下创建名称为User.xml,名称空间必须是UserMapper的全包名路径
<?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.legend.mybatis.mapper.UserMapper">
<!--添加用户-->
<insert id="insertUser" parameterType="com.legend.mybatis.pojo.User">
insert into user (username, birthday, address, sex) values (#{username}, #{birthday}, #{address}, #{sex})
</insert>
<!--删除用户-->
<delete id="deleteUserById" parameterType="Integer">
delete from user where id = #{value}
</delete>
<!--更新用户-->
<update id="updateUserById" parameterType="com.legend.mybatis.pojo.User">
update user set username=#{username}, sex = #{sex}, birthday=#{birthday}, address=#{address}
where id = #{id}
</update>
<!--查找用户-->
<select id="findUserById" parameterType="Integer" resultType="com.legend.mybatis.pojo.User">
select * from user where id = #{value}
</select>
</mapper>
3、编写测试类MapperTest进行测试
public class MapperTest {
// 新增用户
@Test
public void insertUser() throws IOException {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("legend");
user.setAddress("中国");
user.setSex("男");
user.setBirthday(new Date());
mapper.insertUser(user);
sqlSession.commit();
sqlSession.close();
}
// 删除用户
@Test
public void deleteUserById() throws IOException {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUserById(32);
sqlSession.commit();
sqlSession.close();
}
// 修改用户
@Test
public void updateUserById() throws IOException {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(26);
user.setUsername("legend");
user.setAddress("中国");
user.setSex("男");
user.setBirthday(new Date());
mapper.updateUserById(user);
sqlSession.commit();
sqlSession.close();
}
// 查找用户
@Test
public void findUserById() throws IOException {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserById(26);
System.out.println(user);
sqlSession.close();
}
}
Mybatis标签
输入参数
- pojo包装类
1、首先在SqlMapConfig.xml中配置引入pojo包下的所有类,方便后续使用
<typeAliases>
<package name="com.legend.mybatis.pojo"/>
</typeAliases>
2、创建包装类对象,其中包含User对象
/**
* 包装类对象为什么要序列化:
* 因为对象在创建时是存在于内存中,当对象传输的时候会先转换为二进制进行传输,
* 当对方拿到二进制数据后又转换为对象存在于内存中使用,中间有序列化和反序列化的过程。
*/
public class QueryVo implements Serializable {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
3、创建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.legend.mybatis.mapper.UserMapper">
<!--根据用户名模糊查询-->
<select id="findUserByQueryVo" parameterType="QueryVo" resultType="User">
select * from user where username like "%"#{user.username}"%"
</select>
</mapper>
4、编写Mapper接口,并创建Usermapper.xml中的方法
public interface UserMapper {
// 根据用户名模糊查询
List<User> findUserByQueryVo(QueryVo queryVo);
}
5、编写测试代码进行测试
// 根据用户名模糊查询
@Test
public void findUserByQueryVo() throws Exception {
// 1、加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("五");
vo.setUser(user);
List<User> users = mapper.findUserByQueryVo(vo);
for(User u : users) {
System.out.println(u);
}
}
输出参数
输出参数这里只演示简单参数和ResultMap对象,关于输出pojo对象和pojo列表参数上方增删改查的例子。
- 简单类型
1、创建UserMapper.xml,并编写查询用户总数的语句
<!--查询用户总记录数-->
<select id="queryCount" resultType="Integer">
select count(*) from user;
</select>
2、对应的Mapper接口中的方法
public interface UserMapper {
// 查询用户总数
Integer queryCount();
}
3、编写测试类进行测试
// 查询用户总数
@Test
public void queryUserCount() throws Exception {
// 1、加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer count = mapper.queryCount();
System.out.println(count);
}
- ResultMap
ResultType可以指定将查询结果映射为pojo,但是需要pojo属性名和被查询表的字段名一致才能映射成功。如果被查询表的字段名和pojo属性不一致的话,可以通过resultMap将字段名和属性名建立对应关系。
范例:查询订单表order的所有数据。
1、创建pojo类Orders,其中只有userId和数据库字段名不一致
public class Orders implements Serializable{
private Integer id;
private Integer user_id;
private String number;
private Date createtime;
private String note;
...省略Get和Set方法...
@Override
public String toString() {
return "Orders [id=" + id + ", user_id=" + user_id + ", number=" + number + ", createtime=" + createtime
+ ", note=" + note + "]";
}
}
2、创建OrderMapper.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.legend.mybatis.mapper.OrderMapper">
<select id="selectOrdersList" resultType="com.legend.mybatis.pojo.Orders">
select id, user_id, number, createtime, note from orders
</select>
</mapper>
3、在SqlMapConfig中引入OrderMapper.xml文件
<!--Mapper的位置-->
<mappers>
<mapper resource="sqlmap/OrderMapper.xml"></mapper>
</mappers>
4、编写OrderMapper接口,并实现OrderMapper.xml中定义的方法
public interface OrderMapper {
// 查询订单表Order的所有数据
List<Orders> selectOrdersList();
}
5、编写测试代码
// 查询订单表orders的所有数据
@Test
public void selectOrdersList() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> orders = mapper.selectOrdersList();
for (Orders order : orders) {
System.out.println(order);
}
}
输出结果:
Orders [id=3, userId=null, number=1000010, createtime=Wed Feb 04 13:22:35 CST 2015, note=null]
Orders [id=4, userId=null, number=1000011, createtime=Tue Feb 03 13:22:41 CST 2015, note=null]
Orders [id=5, userId=null, number=1000012, createtime=Thu Feb 12 16:13:23 CST 2015, note=null]
由于上边的mapper.xml中sql查询列(user_id)和Order类属性(userId)不一致,所以查询结果不能映射到pojo中。需要定义resultMap,把orderResultMap
和sql查询列(user_id)和Order类属性(userId)对应起来:
<?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.legend.mybatis.mapper.OrderMapper">
<!--手动映射属性名和字段名-->
<resultMap id="orderResultMap" type="Orders">
<!--映射主键-->
<id column="id" property="id"/>
<!--映射表名-->
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
</resultMap>
<select id="selectOrdersList" resultMap="orderResultMap">
select id, user_id, number, createtime, note from orders
</select>
</mapper>
当然,我们还可以把能映射上的全部删除,只保留和数据库表名不一致的属性名的映射
<?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.legend.mybatis.mapper.OrderMapper">
<!--手动映射属性名和字段名-->
<resultMap id="orderResultMap" type="Orders">
<!--映射表名-->
<result column="user_id" property="userId"/>
</resultMap>
<select id="selectOrdersList" resultMap="orderResultMap">
select id, user_id, number, createtime, note from orders
</select>
</mapper>
输出结果:
Orders [id=3, userId=1, number=1000010, createtime=Wed Feb 04 13:22:35 CST 2015, note=null]
Orders [id=4, userId=1, number=1000011, createtime=Tue Feb 03 13:22:41 CST 2015, note=null]
Orders [id=5, userId=10, number=1000012, createtime=Thu Feb 12 16:13:23 CST 2015, note=null]
动态SQL
- if-where语句
范例:根据性别和名字查询用户
1、在UserMaper.xml中编写如下语句:
<!--根据性别和名字查询用户-->
<select id="findUserBySexAndUsername" resultType="User">
select * from user
where
<if test="sex != null and sex != ''">
sex = #{sex}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
</select>
2、UserMapper接口中的方法
// 对应的UserMapper中的方法
public interface UserMapper {
// 根据性别和名字查询用户
List<User> findUserBySexAndUsername(User user);
}
3、编写测试类进行测试
@Test
public void findUserBySexAndUsername() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
//user.setUsername("张三丰");
user.setSex("1");
List<User> users = mapper.findUserBySexAndUsername(user);
for (User u : users) {
System.out.println(u);
}
}
输出结果:
User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市]
User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州]
User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州]
User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州]
User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州]
如果加上user.setUsername(“张三丰”)的话,就会查询性别为1并且名称为张三丰的,则只有一条记录
User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州]
如果我们注释掉user.setSex(“1”),然后打开user.setUsername(“张三丰”),再次执行会出现SQL语法错误
@Test
public void findUserBySexAndUsername() throws Exception {
// 1、加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("张三丰");
//user.setSex("1");
List<User> users = mapper.findUserBySexAndUsername(user);
for (User u : users) {
System.out.println(u);
}
}
运行结果:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL
select * from user where and username = ?
所以我们可以使用where标签来解决出现的问题:
<!--根据性别和名字查询用户-->
<select id="findUserBySexAndUsername" resultType="User">
select * from user
<where>
<if test="sex != null and sex != ''">
sex = #{sex}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
</where>
</select>
注意:where标签是去掉第一个前and,不能去除后and。
-
SQL片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到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="com.legend.mybatis.mapper.UserMapper">
<!--将重复的sql语句提取-->
<sql id="selector">
select * from user
</sql>
<!--根据性别和名字查询用户-->
<select id="findUserBySexAndUsername" resultType="User">
<include refid="selector"/>
<where>
<if test="sex != null and sex != ''">
sex = #{sex}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
</where>
</select>
</mapper>
- foreach标签
范例:根据多个id查询用户信息
1、根据多个id查询的话,我们在Mapper接口中定义方法就要考虑三种参数情况:
public interface UserMapper {
// 根据多个id查询用户信息
public List<User> selectUsersByIds(Integer[] ints);
public List<User> selectUsersByIds(List<Integer> ints);
public List<User> selectUsersByIds(QueryVo vo);
}
2、这里首先来演示第三种,通过包装类传参的方式,所以我们必须在包装类中定义如下属性:
public class QueryVo implements Serializable {
private User user;
private Integer[] ints;
private List<Integer> intLists;
...省略Get和Set方法...
}
3、然后编写Mapper.xml中的sql语句:select * from user WHERE id in ( ? , ? , ? ) 根据该语句拼接
<!--根据多个id查询用户信息-->
<select id="selectUsersByIds" parameterType="QueryVo" resultType="User">
select * from user
<where>
<!--
collection:需要迭代的集合或数组名称
item:表示迭代的项
separator:表示分隔符
open:表示语句的前缀部分
close:表示语句的后缀部分
-->
<foreach collection="intLists" item="id" separator="," open="id in (" close=")">
#{id}
</foreach>
</where>
</select>
4、编写测试类测试
// 根据多个id查询用户
@Test
public void selectUsersByIds() throws Exception {
// 1、加载核心配置文件
InputStream in = Resources.getResourceAsStream( "sqlMapConfig.xml");
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Integer> lists = new ArrayList<>();
lists.add(22);
lists.add(26);
lists.add(34);
QueryVo queryVo = new QueryVo();
queryVo.setIntLists(lists);
List<User> users = mapper.selectUsersByIds(queryVo);
for (User user : users) {
System.out.println(user);
}
}
如果我们传入的参数不是Queryvo包装类,而是array和list的话,只需要collection属性改为array或list:
<!--根据多个id查询用户信息-->
<select id="selectUsersByIds" parameterType="QueryVo" resultType="User">
select * from user
<where>
<foreach collection="array" item="id" separator="," open="id in (" close=")">
#{id}
</foreach>
</where>
</select>
<!--根据多个id查询用户信息-->
<select id="selectUsersByIds" parameterType="QueryVo" resultType="User">
select * from user
<where>
<foreach collection="list" item="id" separator="," open="id in (" close=")">
#{id}
</foreach>
</where>
</select>
总结:
1、当使用包装类作为参数时,collection属性填包装类的引用名称。
2、当使用Array和List作为参数时,需要传递Array和List作为名称。
关联查询
因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,
因为一个用户可以下多个订单。
一对一查询
范例:查询所有订单信息,关联查询下单用户信息。
1、一对一的话,我们需要在Order类中创建User对象,也就是一个User对象对应多个Oders
public class Orders implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
// 附加用户对象
private User user;
...省略Get和Set方法...
}
2、编写一对一的sql语句
<resultMap type="Orders" id="result">
<result column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<!-- 一对一关联 -->
<association property="user" javaType="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
</association>
</resultMap>
<select id="selectOrders" resultMap="result">
SELECT
o.id,
o.user_id,
o.number,
o.createtime,
u.username
FROM orders o
left join user u
on o.user_id = u.id
</select>
测试代码如下所示:
@Test
public void selectOrders() throws Exception {
// 1、加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> orders = mapper.selectOrders();
for (Orders order : orders) {
System.out.println(order);
}
}
一对多查询
1、用户信息和订单信息为一对多关系。也就是说一个用户中包含多个订单信息
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
// 附加对象List表示多个订单
private List<Orders> ordersList;
...省略Get和Set方法...
{
2、编写多对一的sql语句
<resultMap type="User" id="result">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<!-- 一对多 -->
<collection property="ordersList" ofType="Orders">
<id column="id" property="id"/>
<result column="number" property="number"/>
</collection>
</resultMap>
<select id="selectUserList" resultMap="result">
select
o.id,
o.user_id,
o.number,
o.createtime,
u.username
from user u
left join orders o
on o.user_id = u.id
</select>
3、编写测试代码测试
@Test
public void selectUserList() throws Exception {
// 1、加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
// 2、创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3、创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
// 4、动态代理创建Mapper类
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<User> users = mapper.selectUserList();
for (User user : users) {
System.out.println(user);
}
}