Mybatis中CRUD
补充
1. 更新操作
上面我们可以看到id为4的那一行的age、bir都变为了空,有时候我们想让不存在值的保留原来的值,那怎么做呢,我们往下看
使用标签去掉多余的逗号
UserDAO.xml中需要添加的内容:
<!--更新方法-->
<update id="update" parameterType="com.baizhi.eneity.User">
update t_user
<set> <!--set标签动态去掉赋值语句前后多余的逗号-->
<!--test里面书写的name为对象的属性名-->
<!--name不为空并且name不等于空串-->
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="age!=null">
age=#{age},
</if>
<if test="bir!=null">
bir=#{bir}
</if>
</set>
where id=#{id}
<!--
update t_user set name='小李', where id=1
逗号多余了
-->
</update>
在UserDAO接口中需要添加更新方法:
public interface UserDAO {
// 保存用户
int save(User user);
// 更新方法
int update(User user);
}
执行的更新操作的代码:
// 更新操作
@Test
public void update() throws IOException {
// 读取配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// 创建sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// 获取sqlSession执行sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 更新数据:
// 更新:有值更新,不存在值保留原始的值 1.先查(拿)再改 2.动态sql
User user = new User();
user.setId(1);
user.setName("小李");
int count = userDAO.update(user);
System.out.println("修改的条数:" + count);
sqlSession.commit(); // 事务提交
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback(); // 事务回滚
} finally {
sqlSession.close();
}
}
2. 删除操作
在UserDAO接口中需要添加删除方法:
public interface UserDAO {
// 保存用户
int save(User user);
// 更新方法
int update(User user);
// 删除方法
int delete(Integer id);
}
UserDAO.xml中需要添加的内容:
<!--删除方法-->
<!--全限定名应该是:java.lang.Integer,但是在java中lang包默认引入,所以直接写Integer就行-->
<delete id="delete" parameterType="Integer">
delete from t_user where id=#{id}
</delete>
执行的删除操作的代码:
// 删除操作
@Test
public void delete() throws IOException {
// 读取配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// 创建sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// 获取sqlSession执行sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
int delete = userDAO.delete(1);
System.out.println("删除的条数:" + delete);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
sqlSession.close();
}
}
在执行代码时前面有几行数据还有后面的释放资源每次都是一样的,存在冗余,我们可以这些行为封装为工具类
工具类中的代码:
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
// SqlSessionFactory的实例很大,我们可以使用静态代码块简化
// 静态代码块特点:类加载时候执行,只执行一次
static {
// 读取配置文件
InputStream is = null;
try {
is = Resources.getResourceAsStream("mybatis-config.xml");
// 创建sqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 提供sqlSession
public static SqlSession getSqlSession() throws IOException {
// 获取sqlSession执行sql语句
return sqlSessionFactory.openSession();
}
// 关闭sqlSession
public static void close(SqlSession sqlSession){
sqlSession.close();
}
}
3. 查询操作
查询所有
在UserDAO接口中需要添加查询所有方法:
public interface UserDAO {
// 保存用户
int save(User user);
// 更新方法
int update(User user);
// 删除方法
int delete(Integer id);
// 查询所有方法
List<User> queryAll();
}
UserDAO.xml中需要添加的内容:
!--
查询所有
resultType:返回结果,虽然queryAll()方法返回类型为List集合,
但是resultType要写为List集合中封装的泛型类型的全限定名:com.baizhi.eneity.User
resultType: List集合的泛型类型 com.baizhi.eneity.User
-->
<select id="queryAll" resultType="com.baizhi.eneity.User">
select id, name, age, bir from t_user;
</select>
执行的查询所有操作的代码:
// 查询所有
@Test
public void testQueryAll() throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// queryAll()方法返回类型为List集合,mybatis会将数据库中的记录自动封装为User并存储到List集合中
List<User> users = userDAO.queryAll();
// 使用lambda表达式进行List集合的遍历
users.forEach(user -> System.out.println(user));
MybatisUtil.close(sqlSession);
}
查询结果:
关于标签的使用
<!--
查询所有
resultType: List集合的泛型类型 com.baizhi.User
-->
<!--sql标签:用来实现sql语句复用 id:相当于给sql标签中内容定义一个唯一标识-->
<sql id="userQuery">
select id, name, age,bir from t_user
</sql>
<select id="queryAll" resultType="com.baizhi.eneity.User">
<!--使用sql语句的复用的查询所有可以这样写-->
<!--include:包含哪个片段 refid:包含片段的id-->
<include refid="userQuery"/>
</select>
查询一个基于id查询
在UserDAO接口中需要添加查询一个(基于id)方法:
// 根据id查询一个用户
User queryById(Integer id);
UserDAO.xml中需要添加的内容:
<!--sql片段复用-->
<sql id="userQuery">
select id, name, age,bir from t_user
</sql>
<!--查询一个基于id-->
<select id="queryById" parameterType="Integer" resultType="com.baizhi.eneity.User">
<include refid="userQuery"/> where id=#{id}
</select>
执行的查询一个(基于id)的代码:
// 查询一个基于id查询
@Test
public void testQueryById() throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
User user = userDAO.queryById(7);
System.out.println(user);
MybatisUtil.close(sqlSession);
}
查询结果:
模糊查询 where name like ‘%张%’
在UserDAO接口中需要添加一个模糊查询的方法:
// 模糊查询
List<User> queryLikeByName(String name);
UserDAO.xml中需要添加的内容:
<sql id="userQuery">
select id, name, age,bir from t_user
</sql>
<!--根据名字模糊查询-->
<select id="queryLikeByName" parameterType="String" resultType="com.baizhi.eneity.User">
<!--
这样写是错误的
<include refid="userQuery"/> where name like '%#{name}%'
在mybatis中应该使用concat拼接。
oracle: '%' || #{name} || '%'
mysql: concat('%', #{name}, '%')
-->
<include refid="userQuery"/> where name like concat('%', #{name}, '%')
</select>
执行的模糊查询的代码:
// 模糊查询
@Test
public void testQueryLike() throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
List<User> users = userDAO.queryLikeByName("三");
users.forEach(user -> System.out.println(user));
MybatisUtil.close(sqlSession);
}
查询结果:
分页查询 select … from … limit 起始条数,每页显示的记录数
在UserDAO接口中需要添加一个分页查询的方法:
// 分页查询 // 参数1:起始位置 参数2:每页显示的记录数
// 为了在mybatis的层面上区分这两个参数,使用@Param注解给这两个参数起了别名
List<User> queryByPage(@Param("start") Integer start, @Param("rows") Integer rows);
UserDAO.xml中需要添加的内容:
<!--分页查询 多个参数不写parameterType-->
<select id="queryByPage" resultType="com.baizhi.eneity.User">
<include refid="userQuery"/>
limit #{start},#{rows}
</select>
执行的分页查询的代码:
// 分页查询
@Test
public void testQueryPage() throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// mysql默认起始条数是从0开始计数的
// 起始位置:(当前页-1)*每页显示的记录数 该页显示的记录数
List<User> users = userDAO.queryByPage(0, 2);
users.forEach(user -> System.out.println(user));
MybatisUtil.close(sqlSession);
}
结果:
查询总条数
在UserDAO接口中需要添加查询总条数的方法:
// 查询总条数
Long queryTotalCounts();
UserDAO.xml中需要添加的内容:
<!--查询总条数-->
<select id="queryTotalCounts" resultType="Long">
select count(id) from t_user
</select>
执行的查询总条数的代码:
// 查询总条数
@Test
public void testQueryTotalCounts() throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
Long counts = userDAO.queryTotalCounts();
System.out.println("总记录数" + counts);
MybatisUtil.close(sqlSession);
}
结果:
4. resultType 和 resultMap
总结:resultType、resultMap 都是用来对数据库中返回的结果进行封装的
- resultType 只能封装简单类型的对象 简单类型对象:对象中没有对象类型的属性 —> 简单对象(八大、Date、String)
- resultMap 可以封装复杂类型的对象(也可以封装简单的) 处理库表关联关系 --> 一对一、一对多、多对多关系时封装对象
自动封装 对象属性必须和数据库字段一致才会自动封装正确
// 查询所有方法
List<User> queryAll();
<!--
查询所有
resultType: List集合的泛型类型 com.baizhi.User
-->
<!--sql标签:用来实现sql语句复用 id:相当于给sql标签中内容定义一个唯一标识-->
<sql id="userQuery">
select id, name, age,bir from t_user
</sql>
<!--结果映射 id:resultMap标签起一个唯一标识 type:指定封装对象的类型-->
<!--对象属性必须和数据库字段一致才会自动封装,否则就要自己写映射了-->
<resultMap id="userResultMap" type="com.baizhi.eneity.User">
</resultMap>
<select id="queryAll" resultMap="userResultMap">
<!--include:包含哪个片段 refid:包含片段的id-->
<include refid="userQuery"/>
</select>
// 查询所有
@Test
public void testQueryAll() throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
List<User> users = userDAO.queryAll();
// 遍历
users.forEach(user -> System.out.println(user));
MybatisUtil.close(sqlSession);
}
结果:
对象属性和数据库字段不一致会封装错误
<!--
查询所有
resultType: List集合的泛型类型 com.baizhi.User
-->
<!--sql标签:用来实现sql语句复用 id:相当于给sql标签中内容定义一个唯一标识-->
<sql id="userQuery">
select id, name as uname, age,bir from t_user
</sql>
<!--结果映射 id:resultMap标签起一个唯一标识 type:指定封装对象的类型-->
<!--对象属性必须和数据库字段一致才会自动封装,否则就要自己写映射了-->
<resultMap id="userResultMap" type="com.baizhi.eneity.User">
</resultMap>
<select id="queryAll" resultMap="userResultMap">
<!--include:包含哪个片段 refid:包含片段的id-->
<include refid="userQuery"/>
</select>
结果:
解决方法
使用id标签封装主键(如果是联合主键:从联合主键字段中选一列为主键,其余联合主键中的列当做普通列用result标签处理)
使用result标签封装普通列
UserDAO接口中的方法:
// 查询所有方法
List<User> queryAll();
UserDAO.xml中对应的内容:
<!--
查询所有
resultType: List集合的泛型类型 com.baizhi.User
-->
<!--sql标签:用来实现sql语句复用 id:相当于给sql标签中内容定义一个唯一标识-->
<sql id="userQuery">
select id, name as uname, age,bir from t_user
</sql>
<!--结果映射 id:resultMap标签起一个唯一标识 type:指定封装对象的类型-->
<!--对象属性必须和数据库字段一致才会自动封装,否则就要自己写映射了-->
<resultMap id="userResultMap" type="com.baizhi.eneity.User">
<!--主键封装:id标签-->
<!--column:数据库中的列名 property:对象中对应的属性名字-->
<id column="id" property="id"/>
<!--普通列 result标签-->
<result column="uname" property="name"/>
<result column="age" property="age"/>
<result column="bir" property="bir"/>
</resultMap>
<select id="queryAll" resultMap="userResultMap">
<!--include:包含哪个片段 refid:包含片段的id-->
<include refid="userQuery"/>
</select>