只关注sql本身
,而不需要去关注如:注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。详解如下:
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如:注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 1、加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 2、通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
// 3、定义sql语句,?表示占位符
String sql = "select * from user where username = ?";
// 4、获取预处理statement对象
preparedStatement = connection.prepareStatement(sql);
// 5、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "晓艺");
// 6、向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
// 7、遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8、释放资源
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();
}
}
}
}
订单商品案例
来进行讲解。 4.2.1.1、数据库脚本
1、执行sql_table.sql脚本,创建数据库表; 2、执行sql_data.sql初始化测试数据。
4.2.1.2、数据库表 订单商品案例的数据库脚本中,总共包含四张表,其中入门程序只使用user表:
用户表的表结构如下:
Mybatis使用的日志包是log4j的,所以需要添加log4j.properties。 在classpath下创建log4j.properties如下: 文件内容可以从mybatis-3.2.7.pdf中拷贝
# 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
日志级别在开发阶段设置成debug,在生产阶段设置成info或者error。
User.java类如下:
public class User {
private int id;
private String username; // 用户姓名
private String sex; // 性别
private Date birthday; // 生日
private String address; // 地址
// getter和setter方法
在classpath下,创建SqlMapConfig.xml文件,该文件名称不是固定不变的。
SqlMapConfig.xml(文件头可以从mybatis-3.2.7.pdf文档的2.1.2小节中拷贝)
:
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>
<!-- 配置mybatis的环境信息,后面学习中,与spring整合,该信息由spring来管理 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事务控制,由mybatis进行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用mybatis连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
在config目录下,创建普通文件夹sqlmap。在sqlmap目录下,创建User.xml映射文件(这种命名规范是由ibatis遗留下来)。
Mybatis的映射文件头(可以从mybatis-3.2.7.pdf文件中拷贝)
:
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">
4.5.3.1、根据用户ID来查询用户信息 (1)编写映射文件 在User.xml中添加以下代码:
<!--
namespace:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离。
注意:在使用mapper代理开发时,namespace有特殊且重要的作用。
-->
<mapper namespace="test">
<!-- 根据用户ID,查询用户信息 -->
<!--
id:statement的id,要求在命名空间内唯一
parameterType:输入参数的java类型
resultType:查询出的单条结果集对应的java类型
#{}: 表示一个占位符?
#{id}:表示该占位符待接收参数的名称为id。注意:如果参数为简单类型时,#{}里面的参数名称可以是任意定义
-->
<!-- <select id=""></select> 该标签表示一个对象,即该标签最终会封装成一个MappedStatement对象 -->
<select id="findUserById" parameterType="int" resultType="com.itheima.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
(2)在全局配置文件中加载映射文件 在SqlMapConfig.xml中,添加以下代码:
<!-- 加载mapper,即加载映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
(3)编写测试程序
@Test
public void findUserByIdTest() throws IOException {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:输入参数的值,它的类型要和映射文件中对应的statement的输入参数类型一致
// 打印输出结果集
User user = sqlSession.selectOne("test.findUserById", 10);
System.out.println(user);
// 5、关闭资源
sqlSession.close();
}
}
4.5.3.2、根据用户名称模糊查询用户信息列表 (1)编写映射文件 在User.xml中添加以下代码:
<!-- 根据用户名称模糊查询用户信息列表 -->
<!--
${}:表示拼接SQL字符串,${}会原样输出,不加解释
${value}:表示要拼接的是简单类型参数。
注意:
1、如果输入参数为简单类型时,${}里面的参数名称必须为value
2、${}会引起SQL注入,一般情况下不推荐使用。但是有些场景必须使用${},比如 order by ${colname}
-->
<select id="findUsersByName" parameterType="java.lang.String" resultType="com.itheima.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE '%${value}%'
</select>
(2)在全局配置文件中加载映射文件 已配置,此处无需再次配置。
(3)编写测试程序
@Test
public void findUsersByNameTest() throws IOException {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:入参的值,它的类型要和映射文件中对应的statement的入参类型一致
List<User> users = sqlSession.selectList("test.findUsersByName", "小明");
System.out.println(users);
// 5、关闭资源
sqlSession.close();
}
4.5.3.3、添加用户 (1)编写映射文件 在User.xml中添加以下代码:
<!-- 添加用户 -->
<!-- 如果主键的值是通过MySQL自增机制生成的,那么我们此处不需要再显示的给ID赋值
通过OGNL表达式去User对象中查找对应的属性名称获取值,OGNL:对象图导航语言
-->
<insert id="insertUser" parameterType="com.itheima.mybatis.po.User">
INSERT INTO USER(username,sex,birthday,address) VALUES (#{username},#{sex},#{birthday},#{address})
</insert>
(2)在全局配置文件中加载映射文件 已配置,此处无需再次配置。
(3)编写测试程序
@Test
public void insertUserTest() throws IOException {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 构建user参数,没有赋值的属性采取默认值
User user = new User();
user.setUsername("晓艺");
user.setAddress("物资学院地铁站");
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:入参的值,它的类型要和映射文件中对应的statement的入参类型一致
sqlSession.insert("test.insertUser", user);
// 提交事务,切记:增删改操作时,要执行commit操作
sqlSession.commit();
// 5、关闭资源
sqlSession.close();
}
(4)主键返回之MySQL自增主键 当我们做完了添加后,我很想要刚才添加完用户的id,该怎么办呢? 思路:
修改映射文件:
<!-- 添加用户之自增主键返回(selectKey方式) -->
<!--
selectKey标签:通过select查询来生成主键
keyProperty:指定存放生成主键的属性
resultType:生成主键所对应的Java类型
order:指定该查询主键SQL语句的执行顺序,相对于insert语句
last_insert_id:MySQL的函数,要配合insert语句一起使用
-->
<insert id="insertUser1" parameterType="com.itheima.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,sex,birthday,address) VALUES (#{username},#{sex},#{birthday},#{address})
</insert>
(5)主键返回之MySQL函数UUID 注意1:使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,且长度设置成35位。 注意2:只要不是自增主键,那么order属性的值都要设置成BEFORE。
<!-- 添加用户之UUID主键返回 -->
<!--
uuid:MySQL的函数,生成的主键是35位的字符串,所以使用它时要修改数据库表中id的类型为字符类型,且长度设置成35位。
注意:
1、此时order采用BEFORE,因为需要先生成出主键,再执行insert语句
2、需要显式的给ID赋值
-->
<insert id="insertUser2" parameterType="com.itheima.mybatis.po.User">
<selectKey keyProperty="id" resultType="string" order="BEFORE">
SELECT UUID()
</selectKey>
INSERT INTO USER(id,username,sex,birthday,address) VALUES (#{id},#{username},#{sex},#{birthday},#{address})
</insert>
(6)主键返回之Oracle序列返回 序列也就是sequence,它是Oracle的主键生成策略。
<!-- 添加用户之sequence返回 -->
<!--
序列也就是sequence,它是Oracle的主键生成策略。
通过Oracle的sequence获取主键方式与MySQL的uuid方式基本一致。
-->
<insert id="insertUser3" parameterType="com.itheima.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
SELECT seq.nextval() FROM dual
</selectKey>
INSERT INTO USER(id,username,sex,birthday,address) VALUES (#{id},#{username},#{sex},#{birthday},#{address})
</insert>
4.5.3.4、删除用户 (1)编写映射文件 在User.xml中添加以下代码:
<!-- 根据ID删除用户 -->
<delete id="deleteUser" parameterType="int">
DELETE FROM USER WHERE id= #{id}
</delete>
(2)在全局配置文件中加载映射文件 已配置,此处无需再次配置。
(3)编写测试程序
@Test
public void deleteUserTest() throws IOException {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:入参的值,它的类型要和映射文件中对应的statement的入参类型一致
sqlSession.delete("test.deleteUser", 27);
// 提交事务,切记:增删改操作时,要执行commit操作
sqlSession.commit();
// 5、关闭资源
sqlSession.close();
}
4.5.3.5、修改用户 (1)编写映射文件 在User.xml中添加以下代码:
<!-- 根据传入的用户信息修改用户 -->
<update id="updateUser" parameterType="com.itheima.mybatis.po.User">
UPDATE USER SET username = #{username}, sex = #{sex} WHERE id = #{id}
</update>
(2)在全局配置文件中加载映射文件 已配置,此处无需再次配置。
(3)编写测试程序
@Test
public void updateUserTest() throws Exception {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 构建user参数,没有赋值的属性采取默认值
User user = new User();
user.setId(29);
user.setUsername("李晓艺");
user.setSex("女");
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:入参的值,它的类型要和映射文件中对应的statement的入参类型一致
sqlSession.update("test.updateUser", user);
// 提交事务,切记:增删改操作时,要执行commit操作
sqlSession.commit();
// 5、关闭资源
sqlSession.close();
}
#{}和${}
#{}
相当于预处理中的占位符?,#{}接收简单类型的参数时,里面的名称可以任意
#{}
可以接受HashMap、简单类型、POJO类型的参数
#{}
可以防止SQL注入
${}
表示拼接符,${}接收简单类型的参数时,里面的名称必须是value
${}
里面的值会原样输出,不加解释(即:如果该参数值是字符串,也不会添加引号)
${}
存在sql注入的风险,但是有些场景下必须使用,比如排序后面会动态传入排序的列名parameterType和resultType
parameterType 指定输入参数的java类型,parameterType只有一个,也就是说输入参数只有一个。可以填写别名或Java类的全限定名。
resultType 指定输出结果的java类型(注意:是单条记录的java类型),可以填写别名或Java类的全限定名。selectOne 和 selectList
selectOne 表示查询单个对象
selectList 表示查询集合对象
selectList:可以查询0或N条记录
selectOne:只能查询0或1条记录,大于1条记录的话,编译不会报错,但运行会报错,如下图所示:
原始dao开发方式
、mapper代理开发方式(推荐)
。public interface UserDao {
// 根据用户ID来查询用户信息
public User findUserById(int id);
// 根据用户名称来模糊查询用户信息列表
public List<User> findUsersByName(String username);
// 添加用户
public void insertUser(User user);
}
(1)dao实现类代码 注意:需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession,要注意SqlSession和SqlSessionFactory的生命周期。
public class UserDaoImpl implements UserDao {
// 依赖注入SqlSessionFactory,使用构造方法注入
private SqlSessionFactory sqlSessionFactory;
// 使用构造方法来初始化SqlSessionFactory
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) {
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:输入参数的值,它的类型要和映射文件中对应的statement的输入参数类型一致
// 打印输出结果集
User user = sqlSession.selectOne("test.findUserById", id);
System.out.println(user);
// 5、关闭资源
sqlSession.close();
return user;
}
@Override
public List<User> findUsersByName(String username) {
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:入参的值,它的类型要和映射文件中对应的statement的入参类型一致
List<User> users = sqlSession.selectList("test.findUsersByName", username);
System.out.println(users);
// 5、关闭资源
sqlSession.close();
return users;
}
@Override
public void insertUser(User user) {
// 3、根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、使用SqlSession执行statement,并返回映射结果
// 第一个参数:statement的id,建议:namespace.statementId(确保唯一)
// 第二个参数:入参的值,它的类型要和映射文件中对应的statement的入参类型一致
sqlSession.insert("test.insertUser1", user);
System.out.println(user.getId());
// 提交事务,切记:增删改操作时,要执行commit操作
sqlSession.commit();
// 5、关闭资源
sqlSession.close();
}
}
(2)编写测试代码
public class UserDaoTest {
// 声明全局的SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
// 构造UserDao对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
// 调用UserDao对象的方法
User user = userDao.findUserById(28);
System.out.println(user);
}
@Test
public void testFindUsersByName() {
// 构造UserDao对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
// 调用UserDao对象的方法
List<User> users = userDao.findUsersByName("小明");
System.out.println(users);
}
@Test
public void testInsertUser() {
// 构造UserDao对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
// 构造User对象
User user = new User();
user.setUsername("晓艺");
user.setAddress("在物资学院那边");
// 调用UserDao对象的方法
userDao.insertUser(user);
System.out.println(user.getId());
}
(3)问题总结
不过要实现mapper代理的开发方式,需要遵循一些开发规范
。(1)编写mapper映射文件
重新定义mapper映射文件UserMapper.xml(内容同Users.xml,除了namespace的值),放到新创建的普通目录mapper下,该普通目录mapper在config目录下。
即:在config下创建mapper目录然后创建UserMapper.xml(这是mybatis的命名规范,当然,也不是必须是这个名称)。
sqlSession内部的数据区域本身就是一级缓存,是通过map来存储的。
<mapper namespace="com.itheima.mybatis.mapper.UserMapper">
(2)加载mapper映射文件
<!-- 加载mapper,即加载映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
(3)编写mapper接口 内容同UserDao接口一样:
public interface UserMapper {
// 根据用户ID来查询用户信息
public User findUserById(int id);
// 根据用户名称来模糊查询用户信息列表
public List<User> findUsersByName(String username);
// 添加用户
public void insertUser(User user);
}
(4)编写测试代码
public class UserMapperTest {
// 声明全局的SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
// 1、读取全局配置文件(即将全局配置文件读取到输入流中)
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据全局配置文件创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void testFindUsersByName() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findUsersByName("小明");
System.out.println(users);
sqlSession.close();
}
@Test
public void testInsertUser() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造User对象
User user = new User();
user.setUsername("晓艺");
user.setAddress("物资学院");
mapper.insertUser(user);
System.out.println(user.getId());
sqlSession.commit();
sqlSession.close();
}
SqlMapConfig.xml文件中可以引用java属性文件中的配置信息。
db.properties配置信息如下:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
db.username=root
db.password=root
SqlMapConfig.xml使用properties标签后,如下所示:
之前:
<!-- 配置mybatis的环境信息,后面学习中,与spring整合,该信息由spring来管理 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事务控制,由mybatis进行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用mybatis连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
现在:
<!-- 通过properties标签,读取java配置文件的内容或者声明属性信息 -->
<properties resource="db.properties"></properties>
<!-- 配置mybatis的环境信息,后面学习中,与spring整合,该信息由spring来管理 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事务控制,由mybatis进行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用mybatis连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
注意加载顺序:
加载顺序说明:
所以说,Mybatis读取属性的顺序由高到低分别是:parameterType接收的属性值、properties引用的属性、properties标签内定义的属性。
详细参见“mybatis学习资料/mybatis-settings.xlsx”文件,如下图所示:
(1)mybatis支持的别名
(2)自定义别名
<!-- 自定义别名 -->
<typeAliases>
<!-- 单个定义别名 -->
<!-- <typeAlias type="com.itheima.mybatis.po.User" alias="user"/> -->
<!-- 批量定义别名(推荐) -->
<!-- [name]:指定批量定义别名的类包,别名为类名(首字母大小写都可) -->
<package name="com.itheima.mybatis.po"/>
</typeAliases>
详解如下:
(1)<mapper resource=""/>
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml"/>
(2)<mapper url=""/>
使用完全限定路径
如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml"/>
(3)<mapper class=""/>
使用mapper接口的全限定名
如:<mapper class="com.itheima.mybatis.mapper.UserMapper"/>
`注意:此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下。`
(4)<package name=""/>(推荐)
注册指定包下的所有映射文件
如:<package name="com.itheima.mybatis.mapper"/>
`注意:此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下。`
(1)传递简单类型 参考入门需求:根据用户ID查询用户信息。
(2)传递POJO对象 参考入门需求:添加用户。
(3)传递POJO包装对象
需求: 综合查询用户信息,需要传入查询条件复杂,比如(用户信息、订单信息、商品信息)。 用户信息中的查询条件由:用户的名称和性别组成。
一般User.java类要和数据表表字段一致,最好不要在这里面添加其他字段
,第二天学习mybatis的逆向工程时,会根据表结构,生成po类,如果在po类中扩展字段,此时会被覆盖掉。
所以针对要扩展的po类,我们需要创建一个扩展类,来继承它。
定义用户扩展类:
定义POJO包装类:
编写mapper映射文件
<!-- 综合查询,查询用户列表,即:通过传递包装类来进行复杂的用户信息综合查询 -->
<select id="findUserList" parameterType="com.itheima.mybatis.po.UserQueryVO" resultType="userExt">
SELECT * FROM USER WHERE sex = #{userExt.sex} AND username LIKE '%${userExt.username}%'
</select>
注意:入参的类型变为UserQueryVO、结果集的类型变为UserExt,#{}里面的参数变为UserQueryVO对象中的userExt属性的sex和username子属性。
编写Mapper接口
// 综合查询:通过传递包装类来进行复杂的用户信息综合查询
public List<UserExt> findUserList(UserQueryVO userQueryVO);
编写测试代码
@Test
public void testFindUserList() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造UserQueryVO对象
UserQueryVO userQueryVO = new UserQueryVO();
// 构造UserExt对象
UserExt userExt = new UserExt();
userExt.setSex("1");
userExt.setUsername("陈小明");
userQueryVO.setUserExt(userExt);
List<UserExt> list = mapper.findUserList(userQueryVO);
System.out.println(list);
sqlSession.close();
}
(4)传递HashMap(练习) 同传递POJO对象一样,map的key相当于pojo的属性。 编写mapper映射文件
<!-- 综合查询,查询用户列表,即:通过传递HashMap来进行复杂的用户信息查询 -->
<select id="findUserByHashMap" parameterType="java.util.HashMap" resultType="user">
SELECT * FROM USER WHERE sex = #{sex} AND username LIKE '%${username}%'
</select>
上边的sex和username是hashmap的key。 编写Mapper接口
// 综合查询:通过传递HashMap来进行复杂的用户信息综合查询
public List<User> findUserByHashMap(HashMap<String, Object> hashMap);
编写测试代码
@Test
public void testFindUserByHashMap() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造查询条件HashMap对象
HashMap<String, Object> hashMap = new HashMap<String, Object>();
hashMap.put("sex", "1");
hashMap.put("username", "小明");
List<User> list = mapper.findUserByHashMap(hashMap);
System.out.println(list);
sqlSession.close();
}
异常测试: 传递的map中的key和sql中解析的key不一致。 测试结果没有报错,只是通过key获取值为空。
先带着同学们看下原先resultType作为输出结果映射时,它的特点,如何再把列名改为别名,看看是否还能不能映射成功。
使用方法
使用resultType进行结果映射时,需要查询出的列名和映射的pojo属性名完全一致,该列才能映射成功。
如果查询的列名和映射的pojo属性名全部不一致,则不会创建pojo对象,即映射的对象为空。
如果查询的列名和映射的pojo属性名有一个一致,就会创建pojo对象,即映射的对象不为空,但是只有映射正确那一个属性才有值。
如果查询的sql的列名有别名,那么这个别名就是和属性映射的列名。
(1)输出简单类型 当输出结果只有一列时,可以使用ResultType指定简单类型作为输出结果类型。 即:对简单类型的结果映射也是有要求的,查询的列必须是一列,才能映射为简单类型。
<!-- 综合查询时,需要根据综合查询的条件查询用户的总数 -->
<select id="findUserCount" parameterType="UserQueryVO" resultType="int">
SELECT count(*) FROM USER WHERE sex = #{userExt.sex} AND username LIKE '%${userExt.username}%'
</select>
// 综合查询时,需要根据综合查询的条件查询用户的总数,学习:resultType输出简单类型
public int findUserCount(UserQueryVO userQueryVO);
@Test
public void testFindUserCount() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造UserQueryVO对象
UserQueryVO userQueryVO = new UserQueryVO();
// 构造UserExt对象
UserExt userExt = new UserExt();
userExt.setSex("1");
userExt.setUsername("小明");
userQueryVO.setUserExt(userExt);
int count = mapper.findUserCount(userQueryVO);
System.out.println(count);
sqlSession.close();
}
(2)输出POJO单个对象和列表 参考入门程序之:根据用户ID查询用户信息和根据用户名称模糊查询用户列表。 注意:输出单个pojo对象和pojo列表(盛放pojo对象)时,mapper映射文件中的resultType的类型是一样的,只是mapper接口的方法返回值不同。 下面看下mapper接口的不同之处:
// 根据用户ID来查询用户信息
public User findUserById(int id);
// 根据用户名称来模糊查询用户信息列表
public List<User> findUsersByName(String username);
总结: 同样的mapper映射文件,返回单个对象和对象列表时,mapper接口在生成动态代理的时候,会根据返回值的类型,决定调用selectOne方法还是selectList方法。
使用方法 使用resultMap进行结果映射时,不需要查询的列名和映射的属性名必须一致。但是需要声明一个resultMap,来对列名和属性名进行映射。
<!-- resultMap入门 -->
<!-- 声明(定义)resultMap -->
<!--
id:定义resultMap的唯一标识
type:定义该resultMap最终映射的pojo对象
id标签:映射结果集的唯一标识列,如果是多个字段联合唯一,则定义多个id标签
result标签:映射结果集的普通列
column:SQL查询的列名,如果列有别名,则该处填写别名
property:pojo对象的属性名
-->
<resultMap type="user" id="userResultMap">
<id column="id_" property="id"/>
<result column="username_" property="username"/>
<result column="sex_" property="sex"/>
</resultMap>
<!-- 定义statement,根据用户ID来查询用户信息(学习resultMap的使用) -->
<select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
SELECT id id_,username username_,sex sex_ FROM USER WHERE id = #{id}
</select>
图解如下:
// 根据用户ID来查询用户信息(学习resultMap的使用),注意:是通过列的别名进行查询的
public User findUserByIdResultMap(int id);
定义Statement使用resultMap映射结果集时,Mapper接口定义方法的返回值类型为mapper映射文件中resultMap的type类型。
@Test
public void testFindUserByIdResultMap() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserByIdResultMap(1);
System.out.println(user);
sqlSession.close();
}
<!-- 综合查询,查询用户列表,即:通过传递包装类来进行复杂的用户信息综合查询 -->
<select id="findUserList" parameterType="com.itheima.mybatis.po.UserQueryVO" resultType="userExt">
SELECT * FROM USER
<!-- where标签:默认去掉后面第一个AND,如果没有参数,则把自己干掉 -->
<where>
<!-- if标签:可以对输入的参数进行判断 -->
<!-- test:指定判断表达式 -->
<if test="userExt != null">
<if test="userExt.sex != null and userExt.sex != ''">
AND sex = #{userExt.sex}
</if>
<if test="userExt.username != null and userExt.username != ''">
AND username LIKE '%${userExt.username}%'
</if>
</if>
</where>
</select>
<!-- 综合查询时,需要根据综合查询的条件查询用户的总数 -->
<select id="findUserCount" parameterType="UserQueryVO" resultType="int">
SELECT count(*) FROM USER
<!-- where标签:默认去掉后面第一个AND,如果没有参数,则把自己干掉 -->
<where>
<!-- if标签:可以对输入的参数进行判断 -->
<!-- test:指定判断表达式 -->
<if test="userExt != null">
<if test="userExt.sex != null and userExt.sex != ''">
AND sex = #{userExt.sex}
</if>
<if test="userExt.username != null and userExt.username != ''">
AND username LIKE '%${userExt.username}%'
</if>
</if>
</where>
</select>
// 综合查询:通过传递包装类来进行复杂的用户信息综合查询
public List<UserExt> findUserList(UserQueryVO userQueryVO);
// 综合查询时,需要根据综合查询的条件查询用户的总数,学习:resultType输出简单类型
public int findUserCount(UserQueryVO userQueryVO);
输出的SQL如下(也不包含用户名):
另一个测试代码同理,不在赘述! 通过测试可以得知,打印出的SQL语句确实会随着条件的满足情况而不一样。
(1)传递pojo对象中的List集合
@Test
public void testFindUserList() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造UserQueryVO对象
UserQueryVO userQueryVO = new UserQueryVO();
// 构造UserExt对象
UserExt userExt = new UserExt();
// userExt.setSex("1");
// userExt.setUsername("小明");
userQueryVO.setUserExt(userExt);
// 创建用户ID集合,然后设置到QueryUserVO对象中
List<Integer> idList = new ArrayList<Integer>();
idList.add(28);
idList.add(29);
idList.add(30);
userQueryVO.setIdList(idList);
List<UserExt> list = mapper.findUserList(userQueryVO);
System.out.println(list);
sqlSession.close();
}
输出的SQL如下:
(2)直接传递List集合(自学)
<!-- 根据用户ID的集合查询用户列表(学习foreach标签之直接传递ID集合) -->
<select id="findUsersByIdList" parameterType="java.util.List" resultType="userExt">
SELECT * FROM USER
<where>
<if test="list != null and list.size > 0"> <!-- 注意:如果是直接传入集合参数,则该处的参数名称只能填写list -->
AND id IN
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
// 综合查询:根据用户ID的集合查询用户列表(学习foreach标签之传递pojo对象中的List集合)
public List<UserExt> findUsersByIdList(List<Integer> idList);
@Test
public void testFindUsersByIdList() {
// 根据SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建UserMapper对象
// 由Mybatis通过sqlSession来创建动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造List<Integer>集合
List<Integer> idList = new ArrayList<Integer>();
idList.add(28);
idList.add(29);
idList.add(30);
List<UserExt> idList2 = mapper.findUsersByIdList(idList);
System.out.println(idList2);
sqlSession.close();
}