Mybatis高级
1.resultMap
因为数据库设计的规范和Java设计规范的问题会导致数据库列名和java的字段名不一致。不一致就会造成映射失败。
1.1 别名
<select id="findAll" resultType="student"> select student_id as studentId, student_name as studentName, student_gender as studentGender, student_age as studentAge, student_birthday as studentBirthday, student_class as studentClass from student </select>
1.2 驼峰命名转换
<setting name="mapUnderscoreToCamelCase" value="true"/>
缺点:数据库和Java对象相对应才可以使用驼峰转换。
1.3 resultMap
1.3.1 定义resultMap
<resultMap id="BaseResultMap" type="com.xja.pojo.Student"> <!--@mbg.generated--> <!--@Table student--> <id column="student_id" jdbcType="INTEGER" property="studentId" /> <result column="student_name" jdbcType="VARCHAR" property="studentName" /> <result column="student_gender" jdbcType="VARCHAR" property="studentGender" /> <result column="student_age" jdbcType="INTEGER" property="studentAge" /> <result column="student_birthday" jdbcType="TIMESTAMP" property="studentBirthday" /> <result column="student_class" jdbcType="VARCHAR" property="studentClass" /> </resultMap>
1.3.2 使用resultMap
<select id="findAll" resultMap="BaseResultMap"> select * from student </select>
2.分页插件( PageHelper)
2.1 导入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2</version> </dependency>
2.2 配置分页插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
2.3 插件的使用
public void findAll() throws IOException { InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取mapper接口的代理对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); //查询之前需要设置PageHelper //两个参数分别是 页码和每页的大小 PageHelper.startPage(1,3); List<Student> list = mapper.findAll(); //得到前台页面需要的数据 PageInfo<Student> pageInfo = new PageInfo<>(list); System.out.println(pageInfo.getTotal()); System.out.println(pageInfo.getPages()); System.out.println(pageInfo.getPageNum()); System.out.println(pageInfo.getPageSize()); System.out.println(pageInfo.isIsFirstPage()); System.out.println(pageInfo.isIsLastPage()); List<Student> list1 = pageInfo.getList(); list1.forEach(System.out::println); }
3.Mybatis的多表操作
3.1 一对一
3.1.1 方式一 自定义结果集
public class Person { Card card; } //一个人对应一个身份证 在person 类中加入身份证的类作为属性
<select id="findUserInfo" resultMap="userInfo"> select * from user u left join role r on u.userRole =r.id </select>
<resultMap id="BaseResultMap" type="com.xja.pojo.Person"> <id column="person_id" jdbcType="INTEGER" property="personId" /> <result column="person_name" jdbcType="VARCHAR" property="personName" /> <result column="person_age" jdbcType="VARCHAR" property="personAge" /> <result column="card_id" jdbcType="VARCHAR" property="cardId" /> </resultMap> <!--一对一的resultMap--> <resultMap id="personAndCardResultMap" type="com.xja.pojo.Person" extends="BaseResultMap"> <!--引用数据类型的映射(一对一)--> <!--association (关联)--> <association property="card" javaType="com.xja.pojo.Card"> <id property="cardId" column="card_id"/> <result property="homeTown" column="home_town"/> <result property="birthday" column="birthday"/> </association> </resultMap> ------------------------------------------------------------------ autoMapping自动生成映射无需手动添加 <resultMap id="userInfo" type="user" autoMapping="true"> <!-- <id column="id" property="id"></id> 可自动生成,但写上主键的。会提高效率--> <id column="id" property="id"></id> <!--property= + Preson中定义的对象 javaType= + 定义的类型--> <association property="role" javaType="com.xja.pojo.Role" autoMapping="true"> <!--将查询到的role的所有字段全部映射到com.xja.pojo.Role这个实体类的属性上--> <!--同上--> <id column="id" property="id"></id> </association> </resultMap>
嵌套结果集的查询,默认情况下不能自动映射
1、自定义结果集中添加自动映射的属性
添加 autoMapping="true"
2、全局设置mybatis的默认映射级别
在SqlMapConfig.xml 加入配置。
<settings> <setting name="autoMappingBehavior" value="FULL"/> <!--value 属性:NONE 关闭自动映射开关 PARTIAL:对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行 映射,这个也是默认值。 FULL:自动映射所有属性。 --> </settings>
3.1.2 方式二 嵌套查询
先查询user表,返回userrole列该用户角色,可以根据userrole查询role表返回所有的角色信息(将第一次查询的结果作为第二次查询的条件)
public class User { Role role; } //一个用户对应一个身份
public class Person { Card card; }
<select id="find" resultMap="personAndCardResultMap2"> select * from person </select>
<resultMap id="personAndCardResultMap2" type="com.xja.pojo.Person" extends="BaseResultMap"> <association property="card" javaType="com.xja.pojo.Card" column="card_id" select="com.xja.dao.CardMapper.selectByPrimaryKey"> </association> </resultMap>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from card where card_id = #{cardId,jdbcType=VARCHAR} </select>
<!--2.嵌套查询 先查询user表,返回userrole列该用户角色,可以根据userrole查询role表返回所有的角色信息--> <select id="getUserList2" resultMap="getUserInfo2"> select * from user </select> <!--自定义结果集--> <!--角色信息 根据userrole查询角色信息--> <resultMap id="getUserInfo2" type="com.zz.pojo.User" autoMapping="true"> <id property="id" column="id"></id> <!-column根据哪一列数据查询---> <!-select查询的sql语句---> <association property="role" javaType="com.zz.pojo.Role" column="userrole" select="getRoleInfo" autoMapping="true"> <id property="id" column="id"></id> </association> </resultMap> <!--根据角色userrole查询role表返回角色信息--> <select id="getRoleInfo" resultType="com.zz.pojo.Role"> select * from role where id=#{userrole} </select> <!--userrole 上面user表中查询到的userrole-->
public class userTest { mapper.getUserList2() }
无需每一个用户查询一次,有缓存,将user表中查询的信息缓存起来,直接在缓存之中读取出来
3.1.3 mybaits的延迟加载
3.1.3.1 延迟加载的简介
延迟加载:也称之为懒加载,惰性加载。使用懒加载可以提高程序的运行效率。针对数据持久化层的操作可以在某些情况下检查对数据库的不必要的访问。尽量较少sql的执行。
注意:懒加载不支持单表的操作
3.1.3.2 配置懒加载
<!--开启懒加载--> <setting name="lazyLoadingEnabled" value="true"/>
3.2 一对多
3.2.1 方式一
public class Person { List<Score> scores; }
<select id="findStudentAndScore" resultMap="StudentAndScoreResultMap"> select s.*,sc.score_id, sc.subject, sc.score_num from student s left join score sc on s.student_id = sc.student_id </select>
<resultMap id="StudentAndScoreResultMap" type="com.xja.pojo.Student" extends="BaseResultMap"> <collection property="scores" ofType="com.xja.pojo.Score"> <!-- 或使用 <collection property="scores" ofType="com.xja.pojo.Score" autoMapping="true"> --> <id column="score_id" jdbcType="INTEGER" property="scoreId" /> <result column="subject" jdbcType="VARCHAR" property="subject" /> <result column="score_num" jdbcType="DOUBLE" property="scoreNum" /> <result column="student_id" jdbcType="INTEGER" property="studentId" /> </collection> </resultMap>
public class Student { List<Score> scores; }
3.2.2 方式二
<select id="findAll" resultMap="StudentAndScoreResultMap"> select * from student </select>
<resultMap id="StudentAndScoreResultMap" type="com.xja.pojo.Student" extends="BaseResultMap"> <collection property="scores" ofType="com.xja.pojo.Score" column="student_id" select="com.xja.dao.ScoreMapper.findByStudentId"> </collection> </resultMap>
<select id="findByStudentId" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from score <where> <if test="studentId != null"> and student_id=#{studentId,jdbcType=INTEGER}</if> </where> </select> List<Score> findByStudentId(@Param("studentId")Integer studentId);
3.2.3多表查询
<select id="findUserRoleAddress" resultMap="UserAndRoleAndAddressMap"> select * from user u left join role r on u.userRole=r.id left join address a on u.id =a.userId </select> <resultMap id="UserAndRoleAndAddressMap" type="com.xja.pojo.User" autoMapping="true"> //<id property="id" column="id"></id> <!--一对一--> <association property="role" javaType="com.xja.pojo.Role" autoMapping="true"> //<id property="id" column="id"></id> </association> <!--一对多--> <collection property="addresses" ofType="com.xja.pojo.Address" autoMapping="true"> // <id property="id" column="id"></id> </collection> </resultMap> 当多个表的属性相同时,映射时会出错,不写手动映射
多对多的配置和一对多一样。不过在设计表的时候需要加入一个中间表
约定大于配置
3.2.4模糊查询
#和$区别
#等价于PreparedStatment(预编译),使用 ?占位符赋值,防止SQL注入,比较安全
$等价于Statment,直接拼接在SQL语句中使用,不能防止SQL注入,一般用于动态获取表名或者排序的列字段
1.在SQL语句中使用concat 连接符
<select id="getUserByName" resultType="user"> select * from user where username like concat("%",#{name},"%") </select>
2.在SQL语句中使用$符号直接拼接
<select id="getUserByName" resultType="user"> select * from user where username like “%${name}%” </select>
3.在SQL语句中使用bind
<select id="getUserByName" resultType="user"> <bind name="uname" value="'%'+name+'%'"> select * from user where username like #{uname} </select>
4.在java代码中处理参数
3.2.5 基于Mapper的调用和基于sqlSession的调用的区别
@Test public void findAll(){ // UserMapper mapper=sqlSession.getMapper(UserMapper.class); //List<User> all = mapper.getUserName("张"); //all.forEach(System.out::println); //返回的UserMapper的代理对象,就是接口的实现类 //接口中:getUserName(String name); //代理对象 UserMapperProxy //public List<User> getUserByName(String name){ List<User>list=sqlSession.selectList("getUserByName","张"); all.forEach(System.out::println); //} } // // mapper.getUserName("张")相当于 // public List<User> getUserByName(String name){ // List<User>list=sqlSession.selectList("getUserByName","张"); // all.forEach(System.out::println); }
3.3使用结果集嵌套查询结果使用分页插件输出合并数据的问题
<!--一对多查询--> <select id="oneForAll" resultMap="oneForAllResultMap"> select * from USER u left join address a on u.id=a.userId </select> <resultMap id="oneForAllResultMap" type="user" extends="BaseResultMap" > <collection property="addresses" ofType="address" autoMapping="true"> </collection> </resultMap> //当结果集继承其他结果集时,会发生数据的合并 //输出结果 user = User{ addresses=[ Address{id=1, contact='任志强', addressdesc='北京市东城区美术馆后街23号', postcode='100021', tel='13387906742', createdby=1, creationdate=Thu Mar 21 16:52:07 GMT+08:00 2013, userid=1}, Address{id=1, contact='张红丽', addressdesc='北京市海淀区丹棱街3号', postcode='100000', tel='18567672312', createdby=1, creationdate=Thu Mar 21 16:52:07 GMT+08:00 2013, userid=1}, Address{id=1, contact='王丽', addressdesc='北京市东城区东交民巷44号', postcode='100010', tel='13678789999', createdby=1, creationdate=Thu Mar 21 16:52:07 GMT+08:00 2013, userid=1}], role=null, id=1, usercode='admin', username='系统管理员' } //3条合并为一条 user = User{addresses=[Address{id=2, contact='曹颖', addressdesc='北京市朝阳区朝阳门南大街14号', postcode='100053', tel='13568902323', createdby=1, creationdate=Wed Dec 31 19:52:09 GMT+08:00 2014, userid=2}], role=null, id=2, usercode='liming', username='李明'} user = User{addresses=[Address{id=3, contact='王国强', addressdesc='北京市顺义区高丽营镇金马工业区18号', postcode='100061', tel='13787882222', createdby=1, creationdate=Wed Dec 31 19:52:09 GMT+08:00 2014, userid=3}], role=null, id=3, usercode='hanlubiao', username='韩路彪'}
4.Mybatis的注解
mybatis中支持全注解开发。也支持纯XML开发。也支持混合开发。
使用注解开发那么就需要在加载映射文件的时候不能使用加载xml的方式。
4.1 查询所有
@Select("select * from ssm_user") //@Options(useGeneratedKeys = true) //如果需要useGeneratedKeys = true,keyColumn... //等属性时,使用@Options List<User> findAll();
4.1.1模糊查询
@Select("select * from ssm_user where id like concat('%',#{id},'%')) @Select("select * from ssm_user" where id like concat(\"%\",#{id},\"%\"))
4.2 Mybatis参数的处理
参数的类型:基本数据类型和引用数据类型(String,或者自定义对象)
Mybatis解析参数:从一个map中根据key(参数名称)获取,内部mybatis会自动给参数取名Available parameters are [arg1, arg0, param1, param2]
4.2.1 顺序传参法
@Select("select count(*) from ssm_user where user_name=#{param1} and user_password=#{param2}") Integer login(String userName, String userPassword);
4.2.2使用@Param注解
@Select("select count(*) from ssm_user where user_name=#{name} and user_password=#{pwd}") Integer login(@Param("name") String userName, @Param("pwd") String userPassword);
4.2.3 POJO传参法
@Select("select count(*) from ssm_user where user_name=#{userName} and user_password=#{userPassword}") //#{userName} #{}中属性名与类中定义的一致 Integer login(User user);
4.2.4 map传参法
@Select("select count(*) from ssm_user where user_name=#{name} and user_password=#{pwd}") Integer login(Map<String,String> map);
HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("name","lisi"); hashMap.put("pwd","5689"); Integer integer = mapper.login(hashMap);
4.3 新增的注解
@Insert("insert into ssm_user values(0,#{userName},#{userPassword})") void add(User user);
4.4 修改的注解
@Update("update ssm_user set user_name = #{userName},user_password=#{userPassword} where user_id = #{userId}") void updateById(User user);
4.5删除的注解
@Delete("delete from ssm_user where user_id = #{userId}") void deleteById(@Param("userId") Integer userId);
4.6注解开发中的动态sql
不建议使用注解的动态sql
4.6.1 脚本sql
在<Script>标签中完成动态sql的拼接
@Select("<script>select count(*) from ssm_user where 1=1 <if test=\"name!=null\"> and user_name=#{name} </if> <if test=\"pwd !=null\"> and user_password=#{pwd} </if> </script>") Integer login(Map<String,String> map);
4.6.2 在方法中构建SQL
在myabtis3中提供了四个注解
@SelectProvider
@UpdateProvider
@DeleteProvider
@InsertProvider
来解决注解开发中的动态sql问题
@SelectProvider(type = UserMapperProvider.class,method = "loginSQL") Integer login(User user);
public class UserMapperProvider { public String loginSQL(User user){ StringBuffer sql = new StringBuffer("select count(*) from ssm_user where 1=1"); if(user.getUserName()!=null && user.getUserName()!=""){ sql.append(" and user_name = #{userName}"); } if(user.getUserPassword()!=null && user.getUserPassword()!=""){ sql.append(" and user_password = #{userPassword}"); } return sql.toString(); } }
4.7 映射配置
注解开发使用@Results和@Result两个注解来取代xml中resultMap的配置
@Results 等价于之前的resultMap标签 属性: id:唯一表示 等价于 <resultMap id="BaseResultMap> value:里面写的就是@Result注解。等价于xml配置中<id> <result> @Result 等价于之前的<id> <result>标签 属性: id:设置当前的@Result是否映射的是主键 等价于之前的<id> column:列名 property:属性名 one:需要配置@One 等价于xml中的一对一的配置 many:需要配置@Many 等价于xml中的一对多的配置
@Select("select * from ssm_user") @Results(id="userResultMap",value = { @Result(id = true,column = "user_id",property = "userId"), @Result(column = "user_name",property = "userName"), @Result(column = "user_password",property = "userPassword"), }) List<User> findAll();
@Select("select * from ssm_user where user_id = #{userId}") @ResultMap(value = {"userResultMap"}) //引用已经配置的@Results User findById(@Param("userId") Integer userId);
4.8 注解的一对一
public interface PersonMapper { @Select("select * from person") @Results(id="personResultMap",value = { @Result(id = true,column = "person_id",property = "personId"), @Result(column = "person_name",property = "personName"), @Result(column = "person_age",property = "personAge"), @Result(column = "card_id",property = "cardId"), @Result(column = "card_id",property = "card",one = @One(select = "com.xja.dao.CardMapper.selectByPrimaryKey")) }) List<Person> find(); }
public interface CardMapper { @Select("select * from card where card_id = #{cardId}") @Results(id = "cardResultMap",value = { @Result(id = true,column = "card_id",property = "cardId"), @Result(column = "home_town",property = "homeTown"), @Result(column = "birthday",property = "birthday"), }) Card selectByPrimaryK4ey(@Param("cardId") String cardId); }