简介
- 一个orm框架,与Spring Data Jpa分庭抗礼。
- 项目整合Mybatis框架的话大部分公司使用mappers层代替dao层,定义一些增删查改的接口。
代码实例
- 举个栗子,实现学生的增操作
- 先贴jar包:mybatis的jar包、MySQL的驱动包、log4j的jar包
写model包实体类;
public class Student { private Integer id; private String name; private Integer age; //getter、setter }
配置mybatis-config.xml文件引入jdbc资源文件(待写)以配置数据源、给实体类起别名、配置映射器引入数据层接口的映射文件(待写)等;
<?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> <!-- 引入资源文件,推荐 --> <properties resource="jdbc.properties"/> <!-- 直接配置 --> <!-- <properties> <property name="jdbc.driverClassName" value="com.mysql.jdbc.Driver"></property> ... </properties> --> <!-- 给类起别名 --> <typeAliases> <typeAlias alias="Student" type="com.java1234.model.Student"/> </typeAliases> <!-- 推荐,扫描包,全部起类名为别名,不能自定义 --> <!-- <typeAliases> <package name="com.java1234.model"/> </typeAliases> --> <!-- 创建环境 --> <environments default="development"> <!-- 默认环境定义为开发环境,与下面的id对应 --> <!-- 创建一个名为development的环境 --> <environment id="development"> <!-- 由jdbc实现事务管理 --> <transactionManager type="JDBC" /> <!-- 数据源为连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driverClassName}" /> <!-- value取的是jdbc配置文件里的属性 --> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> <!-- 一般还会创建生产、测试等环境 --> </environments> <!-- 映射器 --> <mappers> <!-- 直接找到实体类的映射文件 --> <mapper resource="com/java1234/mappers/StudentMapper.xml" /> <!-- 通过实体类找其配置文件,不要求配置文件也不用和实体类命名相同 --> <mapper class="com.java1234.mappers.StudentMapper"/> <!-- 扫描包下所有配置文件,推荐 --> <package name="com.java1234.mappers"/> </mappers> </configuration> //jdbc.properties jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_mybatis jdbc.username=root jdbc.password=123456
- dataSource
- UNPOOLED:没有连接池,每次数据库操作,mybatis都会创建一个新的连接,用完后关闭;适合小并发项目
- POOLED:常用,可以定连接个数,每次都从连接池里拿一个连接,用完后放回去
- JNDI:使用应用服务器配置JNDI数据源获取数据库连接
- dataSource
util包写session工厂类
/** * session工厂 * @author 14103 */ public class SqlSessionFactoryUtil { //静态域,类的所有实例共享一个sqlSessionFactory,一个对象对其修改则所有对象的该域都被修改 private static SqlSessionFactory sqlSessionFactory; //单例模式:获取session工厂 private static SqlSessionFactory getSqlSessionFactory() { if(sqlSessionFactory==null) { InputStream inputStream=null; try { //把mybatis的配置文件搞成输入流 inputStream=Resources.getResourceAsStream("mybatis-config.xml"); //用这个输入流来创建session工厂 sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); }catch(Exception e) { e.printStackTrace(); } } return sqlSessionFactory; } //打开一个session,相当于打开一个数据库连接 public static SqlSession openSession() { return getSqlSessionFactory().openSession(); //用返回的session工厂打开 } }
mappers包写数据层接口(定义增删查改等接口方法)
/** * 操作数据库的接口 * @author 14103 */ public interface StudentMapper { //返回影响的记录数,默认会返回影响行数的,不用在配置文件定义返回类型 public int add(Student student); public int update(Student student); public int delete(Integer id); public Student findById(Integer id); public List<Student> find(); }
StudentMapper.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"> <!-- 为指定接口中的方法配置sql语句 --> <mapper namespace="com.java1234.mappers.StudentMapper"> <!-- 对应接口的完整路径 --> <!-- 定义一个集合,名为StudentResult --> <resultMap type="Student" id="StudentResult"> <!-- 定义主键 --> <id property="id" column="id"/> <!-- 类属性与表字段对应 --> <!-- 定义普通字段 --> <result property="name" column="name"/> <result property="age" column="age"/> </resultMap> <select id="findStudentWithAddress" resultMap="StudentResult" parameterType="Integer"> select * from t_student t1,t_address t2 where t1.id=#{id} and t2.id=t1.addressId <!-- t2.id=t1.addressId,等号左右调换也一样;去掉t_address的查询也可以; 中括号内是属性而非字段名--> </select> <insert id="add" parameterType="Student"> <!-- id是方法名,后面是入参类型(用的是mybatis配置文件的别名 --> insert into t_student values(#{id},#{name},#{age}) <!-- sql,大括号内是字段;id想要数据库自增的话可以只设置着占位(或者写个null) ,不传值,即实体类可以不setId,但是底层还是会调用占位的所有字段的getter, 所以实体类必须有它们的getter;不想占位可以写成: insert into t_student(name,age) values(#{name},#{age}) --> </insert> <update id="update" parameterType="Student"> update t_student set name=#{name},age=#{age} where id=#{id} </update> <delete id="delete" parameterType="Integer"> delete from t_student where id=#{id} </delete> <select id="findById" parameterType="Integer" resultType="Student"> select * from t_student where id=#{id} </select> <select id="find" resultMap="StudentResult"> <!-- 返回类型为集合 --> select * from t_student </select> </mapper>
如果parameterType(入参类型)是String的话,那么不能用#{xxx}(抛异常:there is no getter for property xxx),而要用#{_parameter}.
<select id="fuzzySearch" resultMap="GoodsResult"> //fuzzySearch只有一个参数且为string类 select * from goods where number=#{_parameter} or name like '%${_parameter}%' or type=#{_parameter} order by Report_Date_Time desc </select>
配置log4j日志
<!--根节点,指定日志等级及输出目标--> log4j.rootLogger=info,appender1,appender2 log4j.appender.appender1=org.apache.log4j.ConsoleAppender log4j.appender.appender2=org.apache.log4j.FileAppender log4j.appender.appender2.File=C:/logFile.txt <!--指定日志输出的布局模式--> log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout log4j.appender.appender2.layout=org.apache.log4j.TTCCLayout
写测试类
/** * 用main做个测试 * @author 14103 */ public class StudentTest { private static Logger logger=Logger.getLogger(StudentTest.class); public static void main(String[] args) { //打开一个数据库连接 SqlSession sqlSession = SqlSessionFactoryUtil.openSession(); //传入一个含有数据库操作的接口,mybatis会把它和配置了sql的xml文件捆绑起来 StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class); Student student=new Student(3,"李四",11); //然后这个接口的方法就能用了 int result=studentMapper.add(student); //直接传入对象,mybatis自动获取属性传入占位符 //提交事务 sqlSession.commit(); if(result>0) { logger.info("添加成功!"); } } }
一对一表关联
- 学生与地址之间的一对一关联
- 分别创建学生类、地址类
写学生类的数据层接口
public interface AddressMapper { public Address findById(Integer id); }
配置映射文件
<mapper namespace="com.java1234.mappers.StudentMapper"> <!-- 对应接口的完整路径 --> <!-- 定义一个返回结果集合,名为StudentResult --> <resultMap type="Student" id="StudentResult"> <!-- 定义主键 --> <id property="id" column="id"/> <!-- 类属性与表字段对应 --> <!-- 定义普通字段 --> <result property="name" column="name"/> <result property="age" column="age"/> <!-- 表关联,推荐;后面三种不推荐 --> <association property="address" column="addressId" select="com.java1234.mappers.AddressMapper.findById"> <!-- 查到目标记录发现addressId字段时,会传入这个select方法中,从而查找到关联的地址并赋给address属性--> </association> <association property="grade" column="gradeId" select="com.java1234.mappers.GradeMapper.findById"> </association> <!-- //对象级联 <result property="address.id" column="addressId"/> //要用主表的外键字段 <result property="address.sheng" column="sheng"/> //要用关联表的字段 <result property="address.shi" column="shi"/> <result property="address.qu" column="qu"/> --> <!-- //嵌套 <association property="address" javaType="Address"> <result property="id" column="id"/> <result property="sheng" column="sheng"/> <result property="shi" column="shi"/> <result property="qu" column="qu"/> </association> --> <!-- //直接关联,需要另外为Address定义一个结果集合(虽然是一对一,但是集合里面只有一个地址也没事的 <association property="address" resultMap="AddressResult"/> --> </resultMap> <!-- <resultMap type="Address" id="AddressResult"> <result property="id" column="id"/> <result property="sheng" column="sheng"/> <result property="shi" column="shi"/> <result property="qu" column="qu"/> </resultMap> --> <select id="findStudentWithAddress" resultMap="StudentResult" parameterType="Integer"> select * from t_student t1,t_address t2 where t1.addressId=t2.id and t1.id=#{id} </select> </mapper>
在学生类的数据层接口中写个查询方法
/** * 操作数据库的接口 * @author 14103 */ public interface StudentMapper { public Student findStudentWithAddress(Integer id); }
在学生类的数据层接口映射文件中绑定sql、配置关联关系
<?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"> <!-- 为指定接口中的方法配置sql语句 --> <mapper namespace="com.java1234.mappers.StudentMapper"> <!-- 对应接口的完整路径 --> <!-- 定义一个返回结果集合,名为StudentResult --> <resultMap type="Student" id="StudentResult"> <!-- 定义主键 --> <id property="id" column="id"/> <!-- 类属性与表字段对应 --> <!-- 定义普通字段 --> <result property="name" column="name"/> <result property="age" column="age"/> <!-- 表关联,需要另外实现Address的数据层接口和映射文件,推荐;后面三种不推荐 --> <association property="address" column="id" select="com.java1234.mappers.AddressMapper.findById"> <!-- column指明子表中与父表关联的字段,查到目标记录发现addressId字段时,会作为下面select方法绑定的t_address表的指定column——id传入方法中,从而查找到关联的地址 --> </association> <!-- //对象级联 <result property="address.id" column="addressId"/> //要用主表的外键字段 <result property="address.sheng" column="sheng"/> //要用关联表的字段 <result property="address.shi" column="shi"/> <result property="address.qu" column="qu"/> --> <!-- //嵌套 <association property="address" javaType="Address"> <result property="id" column="id"/> <result property="sheng" column="sheng"/> <result property="shi" column="shi"/> <result property="qu" column="qu"/> </association> --> <!-- //直接关联,需要另外为Address定义一个结果集合(虽然是一对一,但是集合里面只有一个地址也没事的 <association property="address" resultMap="AddressResult"/> --> </resultMap> <!-- <resultMap type="Address" id="AddressResult"> <result property="id" column="id"/> <result property="sheng" column="sheng"/> <result property="shi" column="shi"/> <result property="qu" column="qu"/> </resultMap> --> <select id="findStudentWithAddress" resultMap="StudentResult" parameterType="Integer"> select * from t_student t1,t_address t2 where t1.addressId=t2.id and t1.id=#{id} </select> </mappers>
- 一对多映射
写个年级类,年级与学生一对多(数据库的话要不要定义成外键都没关系)
public class Grade { private Integer id; private String gradeName; private List<Student> students; //getter、setter @Override public String toString() { return "Grade [id=" + id + ", gradeName=" + gradeName + ", students=" + students + "]"; } }
给年级类写个数据层接口
public interface GradeMapper { public Grade findById(Integer gradeId); }
配置年级类映射文件
<mapper namespace="com.java1234.mappers.GradeMapper"> <!-- 对应接口的完整路径 --> <resultMap type="Grade" id="GradeResult"> <result property="id" column="id"/> <result property="gradeName" column="gradeName"/> <collection property="students" column="id" select="com.java1234.mappers.StudentMapper.findByGradeId"> <!-- 调用下面findById查到年级表的id时传入这个学生表的方法中,查到学生并赋给students属性 --> </collection> </resultMap> <select id="findById" parameterType="Integer" resultMap="GradeResult"> select * from t_grade where id=#{id} </select> </mapper>
给学生类数据层接口加个findByGradeId方法,然后到映射文件中实现该方法
<select id="findByGradeId" resultMap="StudentResult" parameterType="Integer"> select * from t_student where gradeId=#{gradeId} </select>
写个测试
@Test public void testFindGradeWithStudents() { logger.info("查询年级(带学生)"); Grade grade=gradeMapper.findById(1); System.out.println(grade); }
- 目前我们还不能通过学生来获取年级信息,下面我们在学生端也添加年级的获取方式,实现双向关联
学生类加个grade属性,toString加个年级
/*这里要是加个打印grade属性的话,会调用grade类的tostring方法, * 而其中又包含打印students属性,如此往复循环将会报错:StackOverflowError */ @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + ", grade=" + grade.getGradeName() +"]"; }
给学生类映射文件中定义的返回类型StudentResult加个grade属性及其获取方式,这样只要返回类型是StudentResult的查询方法都会获取到grade信息
<association property="grade" column="gradeId" select="com.java1234.mappers.GradeMapper.findById"> <!-- 就算设置了外键也必须写这个方法,否则mybatis是不会自动去查t_grade表的 --> </association>
写个测试
@Test public void testFindStudentWithGrade() { logger.info("查询学生及其地址、年级"); /*借用findStudentWithAddress就能查到年级了, * 但是用findById是查不到的, * 因为映射文件中它的返回类型指定为Student而非StudentResult */ Student student=studentMapper.findStudentWithAddress(2); System.out.println(student); }
- 代码实例:MybatisHelloWorld
动态sql
- if标签:检测不为空的字段进行条件拼接
- choose标签:指定搜索字段,指定了之后就算还有其他字段传进来也不起作用
- where标签:拼接条件时自动把第一个子句的and|or去掉
- trim:提供前后缀、前后缀覆盖方案
- foreach:遍历条件集合
set:自动把参数放进update的坑位里
<select id="searchStudent" parameterType="Map" resultMap="StudentResult"> <!-- 也可以是HashMap之类;传入自定义的类,属性的获取方式也跟下面一样,直接写属性名,而不用调用getter --> select * from t_student where gradeId=#{gradeId} <if test="name!=null">and name like #{name}</if> <!-- like的话等下传进来的name两边要加个% --> <if test="age!=null">and age=#{age}</if> </select> <!-- 淘宝搜索有个下拉框可以指定按照宝贝或店铺来查询 --> <select id="searchStudent2" parameterType="Map" resultMap="StudentResult"> <!-- 也可以是HashMap之类 --> select * from t_student <choose> <when test="searchBy=='gradeId'">where gradeId=#{gradeId}</when> <when test="searchBy=='name'">where name like #{name}</when> <otherwise>where age=#{age}</otherwise> </choose> </select> <select id="searchStudent3" parameterType="Map" resultMap="StudentResult"> <!-- 也可以是HashMap之类 --> select * from t_student <where> <!-- 拼接的时候自动把第一个子句的and去掉 --> <if test="gradeId!=null">gradeId=#{gradeId}</if> <!-- 加不加and随意 --> <if test="name!=null">and name like #{name}</if> <!-- 从第二个if开始就要加and了 --> <if test="age!=null">and age=#{age}</if> </where> </select> <select id="searchStudent4" parameterType="Map" resultMap="StudentResult"> <!-- 也可以是HashMap之类 --> select * from t_student <trim prefix="where" prefixOverrides="and|or"> <!-- 覆盖掉子句的前缀and|or;这样就跟where标签一样了 --> <if test="gradeId!=null">gradeId=#{gradeId}</if> <if test="name!=null">and name like #{name}或者and name like '%${suthor}%'</if> <if test="age!=null">and age=#{age}</if> </trim> </select> <!-- 在某段年级范围内查找学生 --> <select id="searchStudent5" parameterType="Map" resultMap="StudentResult"> <!-- 也可以是HashMap之类 --> select * from t_student <if test="gradeIds!=null"> <where> gradeId in <!-- 把所有元素搞成(?,?,?) --> <foreach collection="gradeIds" item="gradeId" open="(" separator="," close=")"> #{gradeId} </foreach> </where> </if> </select> <update id="updateStudent" parameterType="Student"> update t_student <set> <!-- set能自动把最后一个逗号去掉 --> <if test="name!=null">name=#{name},</if> <!-- 记得加逗号 --> <if test="age!=null">age=#{age},</if> </set> where id=#{id} </update>
字符串型时间的比较、不同类型的判空操作、特殊符号的写法
<!--查询在一下时间段内的记录--> <if test="reportDateTime1!=null">and Report_Date_Time>=#{reportDateTime1}</if> <!--字符型的时间可以直接这样比--> <if test="reportDateTime2!=null">and Report_Date_Time <![CDATA[ <= ]]> #{reportDateTime2}</if> <if test="number!=null">and number=#{number}</if> <!--包装在类中,则number!=0(Goods把number定义为long型,默认值为0,;包装在Map中,则number!=null(Map把它定义为Object,默认值为null)-->
- 代码实例:MybatisActiveSql
处理clog、blob
- 数据库字段对应为longtext、longblob(准确来说是blog,但是它有点小),类属性对应为byte[]、String
给Student类加俩属性
public class Student { private Integer id; private String name; private Integer age; //blob对应为字节数组 private byte[] pic; //clog则是String private String remark; }
数据层接口写个保存和获取数据的方法
<insert id="insert" parameterType="Student"> insert into t_student values(#{id},#{name},#{age},null,null,#{pic},#{remark}) </insert> <select id="getStudentById" parameterType="Integer" resultType="Student"> select * from t_student where id=#{id} </select>
测试方法
@Test public void testInsert() throws IOException { logger.info("添加学生"); Student student=new Student(4,"张三4",14); student.setRemark("很长的文本…"); //插入图片 File file=new File("d://puchijun.jpg"); InputStream bis=new BufferedInputStream(new FileInputStream(file)); //创建数组 byte[] pic=new byte[bis.available()]; //available()是bis的字节长度 //把图片读取到数组 bis.read(pic); bis.close(); student.setPic(pic); //最终放进去的是字节数组 studentMapper.insert(student); sqlSession.commit(); } @Test public void testGetStudentById() throws IOException { logger.info("通过id查找学生"); Student student=studentMapper.getStudentById(4); System.out.println(student); byte[] pic=student.getPic(); //获取到的原始类型是字节数组 File file=new File("d://puchijun2.jpg"); OutputStream outputStream=new BufferedOutputStream(new FileOutputStream(file)); outputStream.write(pic); outputStream.close(); }
Mybatis杂项
分页
- 逻辑分页:把所有记录都查询出来,然后取出指定数目,并不是真正的分页
数据层接口
/** * 操作数据库的接口 * @author 14103 */ public interface StudentMapper { //逻辑分页 public List<Student> findStudent(RowBounds rowBounds); }
映射文件
<select id="findStudent" resultMap="StudentResult" flushCache="false" useCache="true"> <!-- 入参类型是RowBounds,定义不了 --> <!--select 默认不清掉缓存、要使用缓存,这俩配置是一样的;其他操作默认清掉缓存--> select * from t_student </select>
测试
@Test public void testFindStudent() { logger.info("逻辑分页查询学生"); //offset就是start,起始下标 int offset=0,limit=3; //Mybatis的逻辑分页:全部查出来放在内存里,然后只读三条,性能差 RowBounds rowBounds=new RowBounds(offset,limit); List<Student> studentList=studentMapper.findStudent(rowBounds); for(Student student:studentList) { System.out.println(student); } }
物理分页:真正的分页
/** * 操作数据库的接口 * @author 14103 */ public interface StudentMapper { //物理分页 public List<Student> findStudent2(Map<String, Object> map); }
<!-- sql原生物理分页 --> <select id="findStudent2" parameterType="Map" resultMap="StudentResult"> select * from t_student <if test="start!=null and size!=null"> limit #{start},#{size} </if> </select>
@Test public void testFindStudent2() { logger.info("物理分页查询学生"); Map<String,Object> map=new HashMap<>(); map.put("start", 3); map.put("size", 3); List<Student> studentList=studentMapper.findStudent2(map); for(Student student:studentList) { System.out.println(student); } }
缓存
- 用于并发量很大的查询操作,但要求服务器内存要高。这样可以减轻数据库压力,提高性能。
- Mybatis默认情况下启用一级缓存,即同一个SqlSession接口对象调用了相同的select语句,会直接从缓存中返回结果,而不是再查询一次数据库。
在映射文件中配置缓存
<!-- 配置缓存: 1、size:表示缓存cache中能容纳的最大元素数。默认是1024; 2、flushInterval:定义缓存刷新周期,以毫秒计; 3、eviction:定义缓存的移除机制;默认是LRU(least recently userd,最近最少使用),还有FIFO(first in first out,先进先出);这俩是Mybatis的算法,推荐前者 4、readOnly:默认值是false,假如是true的话,缓存只能读(select是读,其他是写,但实际也只有select需要用到缓存)。 --> <cache size="1024" flushInterval="60000" eviction="LRU" readOnly="false"/> <select id="findStudent" resultMap="StudentResult" flushCache="false" useCache="true"> <!-- 入参类型是RowBounds,定义不了 --> <!--select 默认不清掉缓存、要使用缓存,这俩配置是一样的;其他操作默认清掉缓存--> select * from t_student </select>
sql注解
CRUD
- 使用注解方便灵活,但支持的功能较xml要少,而且代码重用性不高,所以目前还是xml比较常用
直接在数据库接口用注解绑定sql语句,不需要在映射文件绑定
/** * 操作数据库的接口 * @author 14103 */ public interface StudentMapper { @Insert("insert into t_student values(#{id},#{name},#{age},null,null,null,null)") public int insert(Student student); @Update("update t_student set name=#{name},age=#{age} where id=#{id}") public int update(Student student); @Delete("delete from t_student where id=#{id}") public int delete(int id); @Select("select * from t_student where id=#{id}") public Student getStudentById(Integer id); @Select("select * from t_student") //结果集合映射 @Results( { @Result(id=true,column="id",property="id"), @Result(column="name",property="name"), @Result(column="age",property="age") } ) public List<Student> findStudent(); }
一对一
在地址类的数据层接口写个查找地址的方法
@Select("select * from t_address where id=#{id}") public Address findById(Integer id);
在学生类的数据层接口写个查找学生带地址的方法
@Select("select * from t_student where id=#{id}") @Results( { @Result(id=true,column="id",property="id"), @Result(column="name",property="name"), @Result(column="age",property="age"), @Result(column="addressId",property="address",one=@One(select="com.java1234.mappers.AddressMapper.findById")) } ) public Student selectStudentWithAddress(int id);
一对多
在年级类的数据层接口里写个查询年级带学生的方法
@Select("select * from t_grade where id=#{id}") @Results( { @Result(id=true,column="id",property="id"), @Result(column="gradeName",property="gradeName"), @Result(column="id",property="students",many=@Many(select="com.java1234.mappers.StudentMapper.selectStudentByGradeId")) } ) public Grade findById(Integer id);
在学生类的数据层接口里写个根据年级查找学生的方法
@Select("select * from t_student where gradeId=#{gradeId}") @Results( { @Result(id=true,column="id",property="id"), @Result(column="name",property="name"), @Result(column="age",property="age"), @Result(column="addressId",property="address",one=@One(select="com.java1234.mappers.AddressMapper.findById")) } ) public Student selectStudentByGradeId(int gradeId);
- 现在查询年级就能带学生了,再实现查询学生带年级的双向关联
在学生类的数据层接口写个查询学生带年级的方法
@Select("select * from t_student where id=#{id}") @Results( { @Result(id=true,column="id",property="id"), @Result(column="name",property="name"), @Result(column="age",property="age"), @Result(column="addressId",property="address",one=@One(select="com.java1234.mappers.AddressMapper.findById")), @Result(column="gradeId",property="grade",one=@One(select="com.java1234.mappers.GradeMapper.findById")) } ) public Student selectStudentWithAddressAndGrade(int id);
- 代码实例:MybatisSqlAnnotation
动态sql
- 需要对数据层接口进行实现,但不是通过xml映射文件,而是写一个实现类
首先是数据层接口
public interface StudentMapper { @InsertProvider(type=StudentDynaSqlProvider.class,method="insertStudent") //调用了insertStudent方法就会自动把参数传进去,无论insertStudent有无定义入参 public int insertStudent(Student student); @UpdateProvider(type=StudentDynaSqlProvider.class,method="updateStudent") public int updateStudent(Student student); @DeleteProvider(type=StudentDynaSqlProvider.class,method="deleteStudent") public int deleteStudent(int id); @SelectProvider(type=StudentDynaSqlProvider.class,method="getStudentById") public Student getStudentById(Integer id); @SelectProvider(type=StudentDynaSqlProvider.class,method="findStudents") public List<Student> findStudents(Map<String,Object> map); }
实现类(和接口在同在mappers包下)
public class StudentDynaSqlProvider { //类名一般都这样取 public String insertStudent(final Student student){ //就是得final,这个入参可以写可以不写,但如果是要直接用student.getName()而不是占位符的话就要写 //返回一个动态拼接完的sql return new SQL(){ { INSERT_INTO("t_student"); if(student.getName()!=null){ VALUES("name", "#{name}"); //也可以直接写student.getName(),但是这样写比较好 } if(student.getAge()!=null){ VALUES("age", "#{age}"); } } }.toString(); } public String updateStudent(final Student student){ return new SQL(){ { UPDATE("t_student"); if(student.getName()!=null){ SET("name=#{name}"); } if(student.getAge()!=null){ SET("age=#{age}"); } WHERE("id=#{id}"); } }.toString(); } public String deleteStudent(){ return new SQL(){ { DELETE_FROM("t_student"); WHERE("id=#{id}"); } }.toString(); } public String getStudentById(){ return new SQL(){ { SELECT("*"); FROM("t_student"); WHERE("id=#{id}"); } }.toString(); } public String findStudents(final Map<String,Object> map){ return new SQL(){ { SELECT("*"); FROM("t_student"); StringBuffer sb=new StringBuffer(); if(map.get("name")!=null){ sb.append(" and name like '"+map.get("name")+"'"); //name属性就要自己带上俩%;注:+两边从内到外是一个双引号一个单引号 } if(map.get("age")!=null){ sb.append(" and age="+map.get("age")); } //什么条件都没有就不要拼where语句了 if(!sb.toString().equals("")){ //去掉第一个子句的and WHERE(sb.toString().replaceFirst("and", "")); } } }.toString(); } }
- 这种用得也是比较少