1. 知识回顾
1.1 回顾自定义Mybatis流程分析
自定义mybatis开发流程图1.2 回顾Mybatis环境搭建-以及实现查询所有的功能
- 创建一个maven 工程 ,不使用骨架 。直接选择maven之后点击next即可:
-
填写自己的groupId(域名反写) 和 ArtifactId (项目名称);
-
设置项目的打包方式为 jar ;
<packaging>jar</packaging>
- 导入依赖 ,Mybatis 依赖 、MySql驱动依赖、log4j依赖、junit单元测试依赖;
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
- 编写User实体类 并实现 Serializable 序列化接口
/**
* User实体类
*/
public class User implements Serializable{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 省略setter 和 getter 方法
// 省略 toString方法
}
- 编写 UserDao 持久层接口
public interface UserDao {
/**
* 查询所有用户信息
* @return
*/
List<User> findAll();
}
-
在resources 目录下创建 SqlMapConfig.xml全局配置文件 并 导入相关约束
在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">
- 在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>
<!--配置环境-->
<environments default="mysql">
<!--配置MySQL-->
<environment id="mysql">
<!--配置数据库的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--xml方式配置映射配置文件所在位置-->
<mappers>
<mapper resource="com/lyp/dao/UserDao.xml"/>
</mappers>
</configuration>
- 在 resources 目录下创建 与 UserDao.java 持久层接口目录相同的UserDao.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.lyp.dao.UserDao">
<select id="findAll" resultType="com.lyp.domain.User">
select * from user;
</select>
</mapper>
- 编写测试类
/**
* mybatis回顾 测试类
*/
public class MybatisLookBackTest {
@Test
public void testSelectList() throws IOException {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession session = factory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
session.close();
}
}
2. Mybatis的CRUD操作
2.1 Mybatis的CRUD 之 保存(在上面回顾代码的项目基础上)
- 在UserDao 持久层接口中编写 保存用户信息的方法 saveUser(User user);
void saveUser(User user);
- 在UserDao.xml映射配置文件中编写对应的 insert 标签 使用 parameterType 指定参数类型, 并编写 sql 语句
<insert id="saveUser" parameterType="com.lyp.domain.User">
insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
</insert>
#{} 括号中的属性来自setter 和 getter 方法
- 在测试类中编写对应的测试方法 : 方法中已经将 获取UserDao代理对象的代码抽取到了 测试方法执行之前的方法中。
@Test
public void testSaveUser() {
User user = new User();
user.setUsername("王晓华");
user.setBirthday(new Date());
// 注意这里的字符个数应该与数据库对应
user.setSex("男");
user.setAddress("重庆市巴南区");
// 抽取 获取userDao代理对象的过程 到 测试方法执行之前 , 将释放资源的代码统一写到测试方法执行之后
userDao.saveUser(user);
}
- 抽取获取UserDao代理对象代码 :
// 在测试类中定义如下属性(成员变量)
private InputStream in;
private UserDao userDao;
private SqlSession sqlSession;
// 测试类中定义如下方法
/**
* @Before 在测试方法执行之前执行
*/
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
/**
* @After 在测试方法执行之后执行
*/
@After
public void destroy() {
// 需要提交事务
// sqlSession.commit();
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (sqlSession != null) {
sqlSession.close();
}
}
- 需要注意的是,这里需要手动提交事务:
- 在测试方法执行之后释放资源之前进行事务的提交即可 :
2.2 MyBatis CRUD 之 修改 和 删除
2.2.1 修改操作
- 在UserDao持久层接口中编写修改方法
// 在UserDao 持久层接口中编写修改方法
/**
* 更新用户信息
* @param user
*/
void updateUser(User user);
- 在UserDao.xml映射配置文件中使用update标签 并使用parameterType属性指定参数类型。编写更新的sql
<update id="updateUser" parameterType="com.lyp.domain.User">
update user set username = #{username} , birthday = #{birthday} , sex = #{sex} ,
address = #{address} where id = #{id}
</update>
- 在测试类中编写测试方法测试更新操作,更新操作也是需要进行手动提交事务操作
/**
* 修改
*/
@Test
public void testUpdateUser() {
User user = new User();
user.setId(50);
user.setUsername("mybatis update");
user.setBirthday(new Date());
user.setAddress("重庆市渝北区");
user.setSex("女");
userDao.updateUser(user);
}
2.2.2 删除操作
- 在UserDao持久层接口中编写删除方法
/**
* 删除用户信息
* @param userId
*/
void deleteUser(int userId) ;
- 在UserDao.xml映射配置文件中使用delete标签编写sql语句,并使用parameterType设置传递的参数类型这里的 parameterType 的值可以为 int 、 INT 、INTEGER 、 Integer、java.lang.Integer。sql语句 : id = #{由于传递的参数只有一个,所以这里的名称可以随意}
<!--这里的 parameterType 的值可以为 int 、 INT 、INTEGER 、 Integer、java.lang.Integer -->
<!--sql语句 : id = #{由于传递的参数只有一个,所以这里的名称可以随意}-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{uid}
</delete>
- 在测试类中编写测试方法进行删除测试
/**
* 删除
*/
@Test
public void testDeleteUser() {
userDao.deleteUser(51);
}
2.3 查询一个和模糊查询
2.3.1 查询一个 (根据id查询一个)
- 在UserDao持久层接口中编写方法。
/**
* 根据id查询用户信息
* @param userId
* @return
*/
User selectById(int userId);
- 在UserDao.xml映射配置文件中使用select标签,并使用parameterType 执行参数类型 , 使用resultType='返回值全限定类名'指定返回值类型
<!--根据id查询用户信息-->
<select id="selectById" parameterType="int" resultType="com.lyp.domain.User">
select * from user where id = #{uid}
</select>
- 在测试类中编写测试方法进行测试
/**
* 根据id查询用户信息
*/
@Test
public void testSelectById() {
User user = userDao.selectById(50);
System.out.println(user);
}
2.3.2 根据用户名进行模糊查询
- 在UserDao持久层接口中编写进行模糊查询的方法
/**
* 根据用户名进行模糊查询
* @param username
* @return
*/
List<User> selectByUsername(String username);
- 在UserDao.xml映射配置文件中使用select标签,并使用parameterType 执行参数类型 , 使用resultType='返回值全限定类名'指定返回值类型
<!--根据用户名进行模糊查询-->
<!--在这里进行模糊查询 但是没有写 %% 就需要在传递参数的时候传递进来-->
<select id="selectByUsername" parameterType="string" resultType="com.lyp.domain.User">
select * from user where username like #{name}
</select>
- 在测试类中编写测试方法
/**
* 根据username进行模糊查询
*/
@Test
public void testSelectByUsername() {
List<User> users = userDao.selectByUsername("%王%");
for (User user : users) {
System.out.println(user);
}
}
2.4 查询返回一行一列 和 占位符分析
2.4.1 查询返回数据总条数
- 在UserDao持久层接口中编写查询数据总条数的方法
/**
* 查询数据总条数
* @return
*/
int selectTotal();
- 在UserDao.xml映射配置文件中使用select标签 编写sql语句 ,并使用resultType='' 指定返回值类型
<!--查询数据总条数-->
<select id="selectTotal" resultType="INT">
select count(id) from user;
</select>
- 在测试类中编写测试方法进行测试
/**
* 查询数据总条数
*/
@Test
public void testSelectTotal() {
int total = userDao.selectTotal();
System.out.println("数据总条数: " + total);
}
2.4.2 模糊查询占位符细节
- 可以使用 如下方式代替模糊查询传递参数时需要写上%%;
<!--根据用户名进行模糊查询-->
<!--在这里进行模糊查询 但是没有写 %% 就需要在传递参数的时候传递进来-->
<select id="selectByUsername" parameterType="string" resultType="com.lyp.domain.User">
<!--select * from user where username like #{name}-->
<!--其中的value是固定写法-->
select * from user where username like '%${value}%'
</select>
/**
* 根据username进行模糊查询
*/
@Test
public void testSelectByUsername() {
//List<User> users = userDao.selectByUsername("%王%");
List<User> users = userDao.selectByUsername("王");
for (User user : users) {
System.out.println(user);
}
}
- 进行模糊查询时占位符细节分析:
2.4.3 保存操作的细节
- 保存的时候由于主键自增长的原因,我们是不知道保存时的id的。但是如果我们需要得到当前保存数据的id,可以通过以下的配置
-- 在数据库中插入一条数据并返回 此时插入数据的id
INSERT INTO USER(username,address,sex,birthday) VALUES('testId','重庆市','男','2020-08-30');
SELECT LAST_INSERT_ID();
<!-- 在插入的insert 标签中进行配置 -->
<insert id="saveUser" parameterType="com.lyp.domain.User">
<!--配置获取插入数据之后的id-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select LAST_INSERT_ID();
</selectKey>
insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
</insert>
保存数据后获取保存数据的id
3. MyBatis的参数深入
3.1 OGNL 表达式
-
OGNL : Object Graphic Navigation Language 对象图导航语言;
-
作用 : 它是通过对象的取值方法来获取数据 。在写法上将get省略掉;
-
案例 : 在获取用户名称的时候 , 类中的写法 user.getUsername(); OGNL表达式的写法:user.username;
-
Mybatis中为什么能直接写username,而不是user.username。因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名而直接写属性名称。
3.2 使用实体的包装对象作为查询条件
- 定义一个实体的包装类对象QueryVo作为查询条件
/**
* 实体包装类对象
*/
public class QueryVo {
private User user;
// 省略 setter 和 getter 方法
}
- 在UserDao持久层接口中编写根据包装类对象进行查询的方法;
- 在UserDao.xml映射配置文件中使用select编写查询的语句 ,注意在占位符中写的参数
<!--根据实体的包装类对象进行查询-->
<select id="selectByVo" resultType="com.lyp.domain.User" parameterType="com.lyp.vo.QueryVo">
select * from user where username like #{user.username}
</select>
映射配置文件中属性对应
- 在测试类中编写方法进行测试
/**
* 根据包装类对象进行查询
*/
@Test
public void testSelectByVo() {
User user = new User();
user.setUsername("%王%");
QueryVo vo = new QueryVo();
vo.setUser(user);
List<User> users = userDao.selectByVo(vo);
for (User u : users) {
System.out.println(u);
}
}
3.3 实体类属性名称和数据库列名不一致
-
会出现报错的情况。 这时修改映射配置文件中的属性与实体类对应,增删改操作是可以使用的。但是注意查询操作的时候,将封装不进去了。
-
修改实体类的属性名称如下:
/**
* User实体类
*/
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
// 省略setter 和 getter方法
}
- 在修改映射配置文件中实体类属性之后,添加和修改操作恢复正常,但是查询操作就值得注意。因为返回的结果集将封装不上。但是userName列比较特殊可以封装上,这是因为在windows下MySQL数据库列名是不区分大小写的。但是在linux操作系统下的MySQL是严格区分大小写的。
3.4 解决实体类属性与数据库列名称不对应的问题
3.4.1 方式一 :起别名
- 通过起别名的方式使数据库列名与实体类属性对应上 。
<select id="findAll" resultType="com.lyp.domain.User">
select id as userId , username as userName , birthday as userBirthday ,sex as userSex , address as userAddress from user;
</select>
3.4.2 方式二 : 采用配置的方式
- 使用resultMap标签,定义实体类属性与数据库列名之间的配置映射关系。
<!--编写resultMap的方式解决数据库列名和实体类属性名称不一致的情况-->
<resultMap id="userMap" type="com.lyp.domain.User">
<!--主键字段配置-->
<id property="userId" column="id"/>
<!--非主键配置-->
<result property="userAddress" column="address"/>
<result property="userBirthday" column="birthday"/>
<result property="userName" column="username"/>
<result property="userSex" column="sex"/>
</resultMap>
- 在 select 标签中 使用resultMap 属性设置使用的resultMap 。
<select id="findAll" resultMap="userMap">
select * from user;
</select>
3.5 案例地址
4. 自定义Dao实现类
- 创建新项目 , 复制上面的项目将实体类属性改为与数据库列名一致,删除以封装对象作为查询条件的方法。
4.1 使用自定义实现类的方式 实现 查询所有
- 编写 UserDao接口的实现类 UserDaoImpl.java; 实现其所有的方法 , 并使用构造方法的方式初始化一个SqlSessionFactory对象;
public class UserDaoImpl implements UserDao {
// 需要使用构造函数创建一个SqlSessionFactory 对象
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
/**
* 查询所有
*
* @return
*/
public List<User> findAll() {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
List<User> users = session.selectList("com.lyp.dao.UserDao.findAll");
session.close();
return users;
}
}
- 修改测试类中的配置
/**
* mybatis回顾 测试类
*/
public class MybatisLookBackTest {
private InputStream in;
private UserDao userDao;
/**
* @Before 在测试方法执行之前执行
*/
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
userDao = new UserDaoImpl(factory);
}
/**
* @After 在测试方法执行之后执行
*/
@After
public void destroy() {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 查询所有
*
* @throws IOException
*/
@Test
public void testSelectList() throws IOException {
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
4.2 使用自定义实现类的方式 实现 保存数据
- 在UserDao实现类 UserDaoImpl 中实现 保存的的方法 saveUser。
/**
* 保存
* @param user
*/
public void saveUser(User user) {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
session.insert("com.lyp.dao.UserDao.saveUser", user);
session.commit();
}
4.3 使用自定义实现类的方式 实现 修改数据
- 在UserDao实现类UserDaoImpl中实现 修改方法 updateUser。
public void updateUser(User user) {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
session.update("com.lyp.dao.UserDao.updateUser", user);
session.commit();
}
4.4 使用自定义实现类的方式 实现 删除数据
- 在UserDao实现类UserDaoImpl中实现 删除方法 deleteUser。
public void deleteUser(int userId) {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
session.update("com.lyp.dao.UserDao.deleteUser", userId);
session.commit();
}
4.5 使用自定义实现类的方式 实现 根据id查询
- 在UserDao实现类UserDaoImpl中实现 查询一个(根据id查询)的方法 selectById。
/**
* 查询一个(根据id进行查询)
* @param userId
* @return
*/
public User selectById(int userId) {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
User user = session.selectOne("com.lyp.dao.UserDao.selectById", userId);
session.commit();
return user;
}
4.6 使用自定义实现类的方式 实现 根据用户名进行模糊查询
- 在UserDao实现类UserDaoImpl中实现 根据用户名进行模糊查询 的方法 selectByUsername。
/**
* 根据username进行模糊查询
* @param username
* @return
*/
public List<User> selectByUsername(String username) {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
return session.selectList("com.lyp.dao.UserDao.selectByUsername",username);
}
4.7 使用自定义实现类的方式 实现 查询数据条数
/**
* 查询当前数据库中数据条数
* @return
*/
public int selectTotal() {
// 获取一个sqlSession对象
SqlSession session = factory.openSession();
return session.selectOne("com.lyp.dao.UserDao.selectTotal");
}
5. 使用Dao实现类方式的执行过程分析
非常重要的一张图-分析编写dao实现类Mybatis的执行过程5.1 断点小技巧
断点小技巧 如何进入到实现类中6. properties 标签的使用及细节
-
在使用代理dao的方式的项目 (mybatis_demo_CRUD_04) 中进行配置。
6.1 在标签中直接配置数据库连接信息
- 将原来的数据库配置信息复制到 properties 标签中 ,然后使用${} 使用属性名称进行引用。
<!--使用 properties 配饰文件的方式配置数据库的连接信息-->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<!--在数据源中引用-->
<!--配置环境-->
<environments default="mysql">
<!--配置MySQL-->
<environment id="mysql">
<!--配置数据库的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
在properties标签中直接配置数据库连接信息
6.2 使用 properties配置文件的方式
- 先再类路径 resources 目录下创建 一个编写数据库配置信息的properties配置文件。内容如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=xxxx
- 在properties标签上使用,resource属性配置配置文件位置及名称 :
<!--使用 properties 配饰文件的方式配置数据库的连接信息-->
<properties resource="jdbcConfiguration.properties"/>
- 和标签内配置数据库连接信息一样,使用${} 获取到数据库的连接信息:
<!--配置环境-->
<environments default="mysql">
<!--配置MySQL-->
<environment id="mysql">
<!--配置数据库的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<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>
6.3 在引用properties 配置文件两种方式
6.3.1 使用resource 属性
- 使用resource属性是使用相对路径读取的类路径下的文件。
<!--使用 properties 配饰文件的方式配置数据库的连接信息-->
<properties resource="jdbcConfiguration.properties"/>
数据库信息配置文件放在了类路径下
6.3.2 使用 url 属性
- URL: Uniform Resource Locator 统一资源定位符,它是可以标识一个资源的位置;
- 例: localhost:9999/user/userInfo?id=100
- URI: Uniform Resource Identifier 统一资源标识符,它是可以在应用中唯一定位一个资源的位置。
- 例:/user/userInfo?id=100
-
在配置中使用 url 属性配置配置文件的位置:
查看配置文件的绝对路径
<!--
使用 properties 配饰文件的方式配置数据库的连接信息
可以在标签内配置连接数据库的信息,也可以通过属性应引用外部配置文件信息
resource属性: 常用的 。 用于指定配置文件所在位置,是按照指定路径的写法来写的,并且配置文件必须存放在类路径下。
url 属性:是要求按照URl地址的写法来写
URl: Uniform Resource Locator 统一资源定位符,它是可以标识一个资源的位置
URI: Uniform Resource Identifier 统一资源标识符,它是可以在应用中唯一定位一个资源的位置
-->
<properties url="file:///D:/work/idea-core/2020_08_29_Mybatis/mybatis_demo_CRUD_04/src/main/resources/jdbcConfiguration.properties"/>
- properties标签配置的位置:
7. TypeAliases 和 package 标签的使用
- 使用typeAliases 标签 中的typeAlias标签 配置 别名。只能配置domain中类的别名。type属性指定的是实体类的全限定类名,alias属性上指定的是别名,指定之后就不在区分大小写。
<typeAliases>
<!--使用typeAlias 标签指定实体类的别名 简化书写-->
<typeAlias type="com.lyp.domain.User" alias="user"/>
</typeAliases>
- 使用 typeAliases 标签 中的 packge 标签配置别名 , 用于指定要配置别名的包,当指定后,该包下的实体类都会注册别名并且类名就是别名,并且不在区分大小写。
<typeAliases>
<!--使用package标签指定实体类的包名-->
<package name="com.lyp.domain"/>
</typeAliases>
- 在mappers 标签中也有一个package的标签。该标签是用于指定dao接口所在的包。当指定完成之后就不需要再写 mapper 以及 resource 和class了。
<mappers>
<!--<mapper resource="com/lyp/dao/UserDao.xml"/>-->
<!--使用package标签指定持久层接口所在包-->
<package name="com.lyp.dao"/>
</mappers>
- 上面几个标签的使用如下图 :
网友评论