MyBatis进阶
- 其他
- 2025-09-09 13:06:02

日志的使用
我们在使用MyBatis的时候, 其实MyBatis框架会打印一些必要的日志信息, 在开发阶段这些日志信息对我们分析问题,理解代码的执行是特别有帮助的; 包括项目上线之后,我们也可以收集项目的错误日志到文件里面去; 所以我们采用专门的日志系统来处理.
步骤 导入坐标拷贝日志配置文件到项目 讲解• 导入坐标
<!--log start--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency> <!--log end-->• log4j.properties
#日志输出级别,最低级别为DEBUG,输出到stdout #error > warn > info > debug > trace log4j.rootLogger=DEBUG,stdout #输出到控制台 log4j.appender.stdout=org.apache.log4j.ConsoleAppender #可以灵活的指定布局模式 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout #时间 log4j.appender.stdout.layout.ConversionPattern=[%-5p] %t %l %d %rms:%m%n log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=D:\\test\\mm_backend.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS\} %-5p [%t] {%c}-%m%nlog4j.rootLogger=INFO, stdout , R
此句为将等级为 INFO 的日志信息输出到 stdout 和 R 这两个目的地,stdout 和 R 的定义在下面的代码,可 以任意起名。等级可分为 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置 OFF 则不打出任何信息, 如果配置为 INFO 这样只显示 INFO, WARN, ERROR 的 log 信息,而 DEBUG 信息不会被显示,具体讲解可参照 第三部分定义配置文件中的 logger。
使用Mybatis完成CRUD 1.需求使用Mybatis完成CRUD
新增用户步骤AspectJ 切面注解中五种通知注解:@Before、@After、@AfterRunning、@AfterThrowing、@Around blog.csdn.net/u010502101/article/details/78823056
1. UserDao中添加新增方法 public interface UserDao { /** * 添加用户 * @param user 要添加到数据库的数据 * @return 受到影响的行数 */ public int addUser(User user); } 2. 在UserDao.xml中加入新增配置 <insert id="addUser" parameterType="User">//因为配置文件里起了别名,所以这里直接User就行了,不然还是要写全路径。 insert into t_user (username,sex,birthday,address) values (#{username},#{sex},#{birthday},#{address})//占位符见下面根据id查询的xml里面有说明。 </insert> 3. 添加操作测试 public class TestMybatis { private UserDao userDao; private SqlSession sqlSession; private InputStream is; @Before//用这个注解,执行@Test之前会先执行这个方法 public void init() throws IOException { //1.让mybatis框架去加载主配置文件 //1.1 将主配置文件SqlMapConfig.xml转换成字节输入流 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //1.2创建一个SqlSessionFactoryBuilder SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder(); //1.3使用factoryBuilder对象加载字节输入流,创建 SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = factoryBuilder.build(is); //1.4使用sqlSessionFactory对象创建处SqlSession对象 sqlSession = sqlSessionFactory.openSession();//使用了工厂模式 //2.使用sqlSession对象创建处UserDao接口的代理对象 userDao = sqlSession.getMapper(UserDao.class);//使用了动态代理 } @Test public void testAddUser(){ //3.调用userDao代理对象的addUser()方法添加用户 User user = new User(null,"zl","男",new Date(),"成都");//注意!这里是null,pojo里面id必须为Integer类型!int类型不能为null。 userDao.addUser(user); //目标是添加完用户之后,获取到该用户的id,可以在以后的多表中,进行关联查询 System.out.println(user.getUid());//这个说明见下面这个 }//获取从pojo获取 @After//同理,@Test之后执行这个。 public void destroy() throws IOException { //提交事务 sqlSession mit(); //4.关闭资源 sqlSession.close(); is.close(); } } 新增用户 id 的返回值新增用户后, 同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回。 • SelectKey获取主键
<!-- 我们可以发现, 这个 sql 语句中使用#{}字符, #{}代表占位符,我们可以理解是原来 jdbc 部分所学的?,它们都是代表占位符, 具体的值是由 User 类的属性来决定的,前提必须名字pojo里面的名字和占位符里面的一样,才会给匹配上去。然后sql前面的名字是和数据库里字段名匹配。 parameterType 属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。 selectKey标签: 查询主键 keyColumn 表示要查询的列名 keyProperty 表示要赋值的属性名,返回的赋值给pojo了 resultType 表示查询出来的结果的类型 order 表示在前或者在后执行 select last_insert_id() 查询最后一个自增长的id的值 --> <insert id="addUser" parameterType="User"> <selectKey resultType="int" order="AFTER" keyProperty="uid" keyColumn="uid"> select last_insert_id() </selectKey> insert into t_user (username, sex, birthday, address) values (#{username}, #{sex}, #{birthday}, #{address}) </insert> 根据id查询用户 UserDao添加根据id查询的方法
public User findById(Integer id); UserDao.xml文件中新增配置
<select id="findById" parameterType="int" resultType="User"> select * from t_user where uid=#{uid} </select> 修改用户 UserDao添加修改的方法
public void updateUser(User user); UserDao.xml文件中新增配置
<update id="updateUser" parameterType="User"> update t_user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where uid=#{uid} </update> 测试: 先调用根据id查询要修改的人的信息,再修改。所以,xml文件里应该修改所有的信息,在这里不能单个修改。
删除用户 UserDao中添加删除方法
public int deleteById(int id); UserDao.xml文件中新增配置
<delete id="deleteById" parameterType="int"> delete from t_user where uid=#{id} </delete>注意: 数据库删除中间的任意一行后,比如删除第3个。原来顺序是1234,会变成124。就是不是一个完整的顺序了!!!如果想调整成完整的顺序,就需要别的操作来操作一下。
模糊查询 方式一(不推荐使用) UserDao中添加模糊查询方法
public List<User> searchByUsername(String name); UserDao.xml文件中新增配置
<select id="searchByUsername" parameterType="String" resultType="User"> select * from t_user where username like #{name} </select> 测试方法 查询名字里有l的。
@Test public void testSearchByUsername(){ List<User> userList = userDao.searchByUsername("%l%"); for (User user : userList) { System.out.println(user); } } 方式二 UserDao中添加模糊查询方法
public List<User> searchByUsername(String name); UserDao.xml文件中新增配置
<!-- 模糊查询 另外一种在SQL语句中引用方法的参数的方式:${} 1. 引用pojo中的属性: '${属性名}' 2. 引用简单类型的参数: '${value}',但是高版本的mybatis中可以'${任意字符串}' --> <select id="searchByUsername" parameterType="String" resultType="User"> 方式一:<!--select * from t_user where username like "%"#{name}"%"--> 方式二:<!--select * from t_user where username like concat("%",#{name},"%")--> select * from t_user where username like "%${value}%" //方式三:才是最常用。 </select> 测试方法
@Test public void testSearchByUsername(){ List<User> userList = userDao.searchByUsername("l");//上面三个都是传一个l就行。 for (User user : userList) { System.out.println(user); } } #{}与${}的区别 #{}一定不能写在引号里面,${}一定要写在引号里面,因为他是字符串拼接如果是pojo、map类型的参数,无论是#{}还是${}里面都是属性名如果是简单类型的参数,#{}里面可以写任意字符串,但是${}里面只能写value(以前的版本)如果使用#{}引入参数的话,其实是先使用?这个占位符,然后再设置参数;而使用${}引入参数的话,是直接拼接SQL语句 建议尽量使用#{}。 SqlSessionFactory工具类的抽取 步骤: 创建SqlSessionFactoryUtils定义一个openSqlSession ()方法获得sqlSession定义释放资源方法保证SqlSessionFactory只有一个(静态代码块) 实现: package com.gavin.utils; 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 java.io.IOException; import java.io.InputStream; /** * 1. 类加载的时候就创建出来了一个SqlSessionFactory对象 * 2. 我们得声明一个方法,用于创建SqlSession对象,因为每次执行SQL语句的sqlSession对象不能是同一个 */ public class SqlSessionFactoryUtils { private static SqlSessionFactory sqlSessionFactory; static { try{ //1 将主配置文件SqlMapConfig.xml转换成字节输入流 InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2创建一个SqlSessionFactoryBuilder SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder(); //3使用factoryBuilder对象加载字节输入流,创建 SqlSessionFactory对象 sqlSessionFactory = factoryBuilder.build(is); is.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 创建SqlSession对象 * @return */ public static SqlSession openSqlSession(){ return sqlSessionFactory.openSession(); } /** * 提交事务并且关闭SqlSession * @param sqlSession */ public static void commitAndClose(SqlSession sqlSession){ sqlSession mit(); sqlSession.close(); } /** * 回滚事务并且关闭SqlSession * @param sqlSession */ public static void rollbackAndClose(SqlSession sqlSession){ sqlSession.rollback(); sqlSession.close(); } } 测试 public class TestSqlSessionFactoryUtils { @Test public void test1(){ //1. 创建SqlSession对象 SqlSession sqlSession = SqlSessionFactoryUtils.openSqlSession(); //2. 创建UserDao的代理对象 UserDao userDao = sqlSession.getMapper(UserDao.class); //3. 调用userDao对象的findById()方法 System.out.println(userDao.findById(6)); //4. 关闭资源,提交事务 SqlSessionFactoryUtils mitAndClose(sqlSession); } } parameterType深入 传递简单类型单个参数,方法就以简单类型传入即可,那么在映射配置文件中的parameterType的值就是这个简单类型的别名;在SQL语句中引入简单类型的参数#{任意字符串} User findById(int id); select * from t_user where uid=#{id}
传递 pojo 对象 或者 Map 将多个参数封装到一个POJO中,那么在映射配置文件中parameterType的值就是这个POJO的全限定名或者别名; 在SQL语句中引入参数#{POJO的属性名}或者’${POJO的属性名}’将多个参数封装到一个Map中(要封装的参数没有对应的POJO),那么在映射配置文件中parameterType的值是map; 在SQL语句中引入参数#{map的key}或者’${map的key}’ void addUser(User user); void updateUser(Map map); <insert id="addUser" parameterType="User"> insert into t_user (username, sex, birthday, address) values (#{username}, #{sex}, #{birthday}, #{address}) </insert> <update id="updateUser" parameterType="map"> update t_user set username=#{username},sex=#{sex} where uid=#{uid} </update> @Test public void test2() { SqlSession sqlSession = SqlSessionFactoryUtils.openSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User user = new User(null, "麻子", "男", new Date(), "重庆"); mapper.addUser(user); SqlSessionFactoryUtils mitAndClose(sqlSession); } @Test public void test3(){ SqlSession sqlSession = SqlSessionFactoryUtils.openSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); Map map = new HashMap(); map.put("username","老八"); map.put("sex","女"); map.put("uid",7); mapper.updateUser(map); SqlSessionFactoryUtils mitAndClose(sqlSession); } 传递多个参数 方式一:通过参数索引 User findByUsernameAndAddress(String username, String address); <select id="findByUsernameAndAddress" resultType="User"> select * from t_user where username=#{arg0} and address=#{arg1} </select> 方式二:通过注解@Param使用注解指定参数的名称
User findByUsernameAndSex(@Param("name") String username,@Param("sex") String sex); <select id="findByUsernameAndSex" resultType="User"> select * from t_user where username=#{name} and sex=#{sex} </select> 传递 pojo 包装对象类型开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。Pojo 类中包含 pojo。
需求:根据用户id查询用户信息并进行分页,查询条件放到 QueryVo 的 user 属性中。
QueryVo.java
@Data @AllArgsConstructor @NoArgsConstructor public class QueryVo { public QueryVo(Long currentPage, Integer pageSize, User queryCondition) { this.currentPage = currentPage; this.pageSize = pageSize; this.queryCondition = queryCondition; } private Long currentPage; private Integer pageSize; //查询条件 private User queryCondition; private Long offset; public Long getOffset(){ return (currentPage-1)*pageSize; } }UserDao.java
List<User> searchByCondition(QueryVo queryVo);UserDao.xml
<select id="searchByCondition" parameterType="QueryVo" resultType="User"> select * from t_user where sex=#{queryCondition.sex} and address=#{queryCondition.address} limit #{offset},#{pageSize} </select>测试
@Test public void test6() { SqlSession sqlSession = SqlSessionFactoryUtils.openSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User user = new User(); user.setSex("男"); user.setAddress("北京"); QueryVo queryVo = new QueryVo(1l,5,user); List<User> userList = mapper.searchByCondition(queryVo); for (User user1 : userList) { System.out.println(user1); } SqlSessionFactoryUtils mitAndClose(sqlSession); } resultType深入 输出简单类型查询的结果是单个数据, 映射配置文件中的resultType属性的值就是这个数据的类型
/** * 查询用户的总个数 * @return */ Long findTotal(); <select id="findTotal" resultType="long"> select count(*) from t_user </select> 输出pojo对象(一个pojo对象就对应一行数据)或者一个Map查询的结果是一行数据: • 将这一行数据存储到POJO对象中, 映射配置文件的resultType的值就是POJO的全限定名或者别名,此时就要求查询结果的字段名和类型 要和POJO的属性名和类型一致
/** * 根据id查询一条数据 * @param id * @return */ User findById(int id); <select id="findById" parameterType="int" resultType="User"> select * from t_user where uid=#{uid} </select>• 将这一行数据存储到Map对象,映射配置文件的resultType的值就是map,那么此时查询结果中的字段名就是 map的key,字段值就是map的value
/** * 根据用户名查询用户 * @param username * @return */ Map findByUsername(String username); <select id="findByUsername" parameterType="string" resultType="map"> select * from t_user where username = #{username} </select> 输出pojo列表(一个pojo列表就对应多行数据)或者Map的列表查询的结果是多行数据: • 将多条数据存储到List中,映射配置文件的resultType的值就是POJO的别名
List<User> findAll(); <select id="findAll" resultType="User"> select * from t_user </select>• 将多条数据存储到List中,映射配置文件的resultType的值就是map
List<Map> findAll2(); <select id="findAll2" resultType="map"> select * from t_user </select> resultMap结果类型resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。 如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。 resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。(下次课讲) 那我们今天就来看返回的列名与实体类的属性不一致时的情况. 下次课再接着研究复杂的封
UserInfo
package com.gavin.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor public class UserInfo implements Serializable { private Integer userId; private String username; private String userSex; private String userBirthday; private String userAddress; }UserDao
/** * 查询所有用户信息,封装到UserInfo对象 * @return */ List<UserInfo> findAllUserInfo();UserDao.xml
<resultMap id="userInfoMap" type="UserInfo" autoMapping="true"> Column是数据库字段名 <id column="uid" property="userId"></id> <result column="sex" property="userSex"></result> <result column="birthday" property="userBirthday"></result> <result column="address" property="userAddress"></result> </resultMap> <select id="findAllUserInfo" resultMap="userInfoMap"> select * from t_user </select>测试
public class TestMybatis { @Test public void test1() { SqlSession sqlSession = SqlSessionFactoryUtils.openSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<UserInfo> userInfoList = mapper.findAllUserInfo(); for (UserInfo userInfo : userInfoList) { System.out.println(userInfo); } SqlSessionFactoryUtils mitAndClose(sqlSession); } }