MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
JDBC的数据操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Class.forName("com.mysql.jdbc.Driver" ); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test" , "root" , "admin" );String sql = "SELECT * FROM STUDENTS WHERE STUD_ID=?" ;PreparedStatement pstmt = conn.prepareStatement(sql);pstmt.setInt(1 , studId); ResultSet rs = pstmt.executeQuery();if (rs.next()){ student = new Student (); student.setStudId(rs.getInt("stud_id" )); student.setName(rs.getString("name" )); student.setEmail(rs.getString("email" )); student.setDob(rs.getDate("dob" )); }
上述的每个方法中有大量的重复代码:创建一个连接,创建一个Statement对象,设置输入参数,关闭资源(如connection,statement,resultSet)。MyBatis抽象了上述的这些相同的任务,如准备需要被执行的SQLstatement对象并且将Java对象作为输入数据传递给statement对象的任务,进而开发人员可以专注于真正重要的方面。
另外,MyBatis自动化了将从输入的Java对象中的属性设置成查询参数、从SQL结果集上生成Java对象这两个过程。
hibernate映射Java对象到数据库表上,
mybatis则是主要映射查询结果集合以及查询参数,这使得MyBatis可以很好地与传统数据库协同工作。你可以根据面相对象的模型创建Java域对象,执行传统数据库的查询,然后将结果映射到对应的Java对象上。
mybatis特性
当前有很多Java实现的持久化框架,而MyBatis流行起来有以下原因:
它消除了大量的JDBC冗余代码
它有低的学习曲线
它能很好地与传统数据库协同工作
它可以接受SQL语句
它提供了与Spring 和Guice框架的集成支持
它提供了与第三方缓存类库的集成支持
它引入了更好的性能
mybatis配置文件详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 <?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 ="mysql.properties" > <property name ="username" value ="db_user" /> <property name ="password" value ="verysecurepwd" /> </properties > <settings > <setting name ="cacheEnabled" value ="true" /> <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="true" /> <setting name ="multipleResultSetsEnabled" value ="true" /> <setting name ="useColumnLabel" value ="true" /> <setting name ="useGeneratedKeys" value ="false" /> <setting name ="autoMappingBehavior" value ="PARTIAL" /> <setting name ="autoMappingUnknownColumnBehavior" value ="WARNING" /> <setting name ="defaultExecutorType" value ="SIMPLE" /> <setting name ="defaultStatementTimeout" value ="25" /> <setting name ="defaultFetchSize" value ="100" /> <setting name ="safeRowBoundsEnabled" value ="false" /> <setting name ="mapUnderscoreToCamelCase" value ="false" /> <setting name ="localCacheScope" value ="SESSION" /> <setting name ="jdbcTypeForNull" value ="OTHER" /> <setting name ="lazyLoadTriggerMethods" value ="equals,clone,hashCode,toString" /> </settings > <typeAliases > <package name ="com.chenxi.mybatis.beans" /> </typeAliases > <environments default ="mybatis" > <environment id ="development" > <transactionManager type ="JDBC" /> <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 > <environment id ="production" > <transactionManager type ="MANAGED" /> <dataSource type ="JNDI" > <property name ="data_source" value ="java:comp/jdbc/MyBatisDemoDS" /> </dataSource > </environment > </environments > <mappers > <mapper url ="file:///D:/mybatisdemo/app/mappers/TutorMapper.xml" /> <mapper class ="com.mybatis3.mappers.TutorMapper" /> <package name ="com/chenxi/mybatis/mapper" /> </mappers > </configuration >
基于Java API的配置 创建SessionFactory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 SqlSessionFactory sqlSessionFactory = null ;try { DataSource dataSource = DataSourceFactory.getDataSource(); TransactionFactory transactionFactory = new JdbcTransactionFactory (); Environment environment = new Environment ("development" , transactionFactory, dataSource); Configuration configuration = new Configuration (environment); configuration.getTypeAliasRegistry().registerAlias("student" , Student.class); configuration.getTypeAliasRegistry().registerAlias("Student" , "com.mybatis3.domain.Student" ); configuration.getTypeAliasRegistry().registerAlias(Student.class); configuration.getTypeAliasRegistry().registerAliases("com.mybatis3.domain" ); configuration.getTypeAliasRegistry().registerAliases("com.mybatis3.domain" , Identifiable.class); configuration.getTypeHandlerRegistry().register(PhoneNumber. class, PhoneTypeHandler.class); configuration.getTypeHandlerRegistry().register("com.mybatis3.typehandlers" ); configuration.addMapper(StudentMapper.class); configuration.addMappers("com.mybatis3.mappers" ); configuration.addMappers("com.mybatis3.mappers" , BaseMapper.class); sqlSessionFactory = new SqlSessionFactoryBuilder ().build(configuration); }catch (Exception e){ throw new RuntimeException (e); } return sqlSessionFactory;
创建数据源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class DataSourceFactory { public static DataSource getDataSource () { String driver = "com.mysql.jdbc.Driver" ; String url = "jdbc:mysql://localhost:3306/mybatisdemo" ; String username = "root" ; String password = "admin" ; PooledDataSource dataSource = new PooledDataSource (driver, url, username, password); return dataSource; } public static DataSource getJNDIDataSource () { String jndiName = "java:comp/env/jdbc/MyBatisDemoDS" ; try { InitialContext ctx = new InitialContext (); DataSource dataSource = (DataSource) ctx.lookup(jndiName); return dataSource; }catch (NamingException e){ throw new RuntimeException (e); } } }
还可以使用第三方库创建数据源,c3p0、druid等
全局设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 configuration.setCacheEnabled(true ); configuration.setLazyLoadingEnabled(false ); configuration.setMultipleResultSetsEnabled(true ); configuration.setUseColumnLabel(true ); configuration.setUseGeneratedKeys(false ); configuration.setAutoMappingBehavior(AutoMappingBehavior.PARTIAL); configuration.setDefaultExecutorType(ExecutorType.SIMPLE); configuration.setDefaultStatementTimeout(25 ); configuration.setSafeRowBoundsEnabled(false ); configuration.setMapUnderscoreToCamelCase(false ); configuration.setLocalCacheScope(LocalCacheScope.SESSION); configuration.setAggressiveLazyLoading(true ); configuration.setJdbcTypeForNull(JdbcType.OTHER); Set<String> lazyLoadTriggerMethods = new HashSet <String>(); lazyLoadTriggerMethods.add("equals" ); lazyLoadTriggerMethods.add("clone" ); lazyLoadTriggerMethods.add("hashCode" ); lazyLoadTriggerMethods.add("toString" ); configuration.setLazyLoadTriggerMethods(lazyLoadTriggerMethods );
mappper.xml 该文件中的主要几个顶级元素
cache – 配置给定命名空间的缓存。
cache-ref – 从其他命名空间引用缓存配置。
resultMap – 最复杂,也是最有力量的元素,用来描述如何从数据库结果集中来加载你的对象。
parameterMap – 已经被废弃了!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除。这里不会记录。
sql – 可以重用的 SQL 块,也可以被其他语句引用。
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
select元素 1 2 3 <select id ="selectPerson" parameterType ="int" parameterMap ="deprecated" resultType ="hashmap" resultMap ="personResultMap" flushCache ="false" useCache ="true" timeout ="10000" fetchSize ="256" statementType ="PREPARED" resultSetType ="FORWARD_ONLY" > SELECT * FROM PERSON WHERE ID = #{id} </select >
上面的代码约等于jdbc中的下述代码:
1 2 3 4 5 String selectPerson = "SELECT * FROM PERSON WHERE ID=?" ;PreparedStatement ps = conn.prepareStatement(selectPerson);ps.setInt(1 ,id);
insert, update and delete 数据变更语句 insert,update 和 delete 在它们的实现中非常相似:
1 2 3 4 5 6 7 8 9 10 11 <insert id ="insertAuthor" parameterType ="domain.blog.Author" flushCache ="true" statementType ="PREPARED" keyProperty ="" keyColumn ="" useGeneratedKeys ="" timeout ="20000" /> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert > <update id ="updateAuthor" parameterType ="domain.blog.Author" flushCache ="true" statementType ="PREPARED" timeout ="20000" > update Author set username=#{username},password=#{password},email=#{email},bio=#{bio} where id=#{id} </update > <delete id ="deleteAuthor" parameterType ="int" flushCache ="true" statementType ="PREPARED" timeout ="20000" /> delete from Author where id=#{id} </delete >
其中的属性解释如下:
属性
解释
id
在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType
将会传入这条语句的参数类的完全限定名或别名。
parameterMap
这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache
将其设置为 true,不论语句什么时候被带哦用,都会导致缓存被清空。默认值:false。
timeout
这个设置驱动程序等待数据库返回请求结果, 并抛出异常时间的最大等待值。默认不设置(驱动自行处理)。
statementType
STATEMENT,PREPARED或CALLABLE的一种。这会让MyBatis使用选择使用Statement,PreparedStatement或CallableStatement。默认值:PREPARED。
useGeneratedKeys
(仅对insert有用) 这会告诉MyBatis使用JDBC的getGeneratedKeys方法来取出由数据(比如:像MySQL和SQLServer这样的数据库管理系统的自动递增字段)内部生成的主键。默认值:false。
keyProperty
(仅对insert有用)标记一个属性,MyBatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值。默认: 不设置。
keyColumn
(仅对insert有用)标记一个属性,MyBatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值。默认: 不设置。
sql 1 2 3 4 <sql id ="userColumns" > id,username,password </sql > <select id ="selectUsers" parameterType ="int" resultType ="hashmap" > select <include refid ="userColumns" /> from some_table where id = #{id} </select >
property 1 \#{property, mode=OUT,javaType=int,jdbcType=NUMERIC, mode=OUT}
resultMap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <resultMap type ="CoursesBean" id ="coursesMap" > <id property ="id" column ="id" javaType ="java.lang.Integer" /> <result property ="name" column ="courses_name" javaType ="java.lang.String" typeHandler ="" jdbcType ="" /> </resultMap > <resultMap type ="CoursesBean" id ="couAndStu" > <id property ="id" column ="id" javaType ="java.lang.Integer" /> <result property ="name" column ="courses_name" javaType ="java.lang.String" /> <collection property ="student" column ="id" select ="findStudentByCourses" /> <collection property ="student" column ="id" resultMap ="coursesMap" /> </resultMap > <resultMap id ="detailedBlogResultMap" type ="Blog" > <constructor > <idArg column ="blog_id" javaType ="int" /> </constructor > <result property ="title" column ="blog_title" /> <association property ="author" javaType ="Author" > <id property ="id" column ="author_id" /> <result property ="username" column ="author_username" /> <result property ="password" column ="author_password" /> <result property ="email" column ="author_email" /> <result property ="bio" column ="author_bio" /> <result property ="favouriteSection" column ="author_favourite_section" /> </association > <collection property ="posts" ofType ="Post" > <id property ="id" column ="post_id" /> <result property ="subject" column ="post_subject" /> <association property ="author" javaType ="Author" /> <collection property ="comments" ofType ="Comment" > <id property ="id" column ="comment_id" /> </collection > <collection property ="tags" ofType ="Tag" > <id property ="id" column ="tag_id" /> </collection > <discriminator javaType ="int" column ="draft" > <case value ="1" resultType ="DraftPost" /> </discriminator > </collection > </resultMap >
动态Sql语句
mybatis通过ONGL(Object Graph Nagigation Language)表达式来构建动态sql语句
If条件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <resultMap type ="Course" id ="CourseResult" > <id column ="course_id" property ="courseId" /> <result column ="name" property ="name" /> <result column ="description" property ="description" /> <result column ="start_date" property ="startDate" /> <result column ="end_date" property ="endDate" /> </resultMap > <select id ="searchCourses" parameterType ="hashmap" resultMap ="CourseResult" > </select > SELECT * FROM COURSES WHERE TUTOR_ID= #{tutorId} <if test ="courseName != null" > AND NAME LIKE #{courseName} </if > <if test ="startDate != null" > AND START_DATE >= #{startDate} </if > <if test ="endDate != null" > AND END_DATE <= #{endDate} </if > </select >
choose,when 和otherwise 条件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="searchCourses" parameterType ="hashmap" resultMap ="CourseResult" > SELECT * FROM COURSES <choose > <when test ="searchBy == 'Tutor'" > WHERE TUTOR_ID= #{tutorId} </when > <when test ="searchBy == 'CourseName'" > WHERE name like #{courseName} </when > <otherwise > WHERE TUTOR start_date >= now() </otherwise > </choose > </select >
where、set条件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <select id ="searchCourses" parameterType ="hashmap" resultMap ="CourseResult" > SELECT * FROM COURSES <where > <if test =" tutorId != null " > TUTOR_ID= #{tutorId} </if > <if test ="courseName != null" > AND name like #{courseName} </if > <if test ="startDate != null" > AND start_date >= #{startDate} </if > <if test ="endDate != null" > AND end_date <= #{endDate} </if > </where > </select > <update id ="updateStudent" parameterType ="Student" > update students <set > <if test ="name != null" > name=#{name},</if > <if test ="email != null" > email=#{email},</if > <if test ="phone != null" > phone=#{phone},</if > </set > where stud_id=#{id} </update >
条件 1 2 3 4 5 6 7 8 9 10 11 <select id ="searchCourses" parameterType ="hashmap" resultMap ="CourseResult" > SELECT * FROM COURSES <trim prefix ="WHERE" prefixOverrides ="AND | OR" > <if test =" tutorId != null " > TUTOR_ID= #{tutorId} </if > <if test ="courseName != null" > AND name like #{courseName} </if > </trim > </select >
foreach循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <select id ="searchCoursesByTutors" parameterType ="map" resultMap ="CourseResult" > SELECT * FROM COURSES <if test ="tutorIds != null" > <where > <foreach item ="tutorId" collection ="tutorIds" > OR tutor_id=#{tutorId} </foreach > </where > </if > </select > <select id ="searchCoursesByTutors" parameterType ="map" resultMap ="CourseResult" > SELECT * FROM COURSES <if test ="tutorIds != null" > <where > tutor_id IN <foreach item ="tutorId" collection ="tutorIds" open ="(" separator ="," close =")" > #{tutorId} </foreach > </where > </if > </select >
缓存 MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。默认情况下是没有开启缓存的,除了局部的session缓存,可以增强变现而且处理循环依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
这个简单语句的效果如下:
映射语句文件中的所有select语句将会被缓存。
映射语句文件中的所有insert,update和delete语句会刷新缓存。
缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序来刷新。
缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用。
缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
所有的这些属性都可以通过缓存元素的属性来修改。比如:
1 2 3 4 5 6 <cache eviction ="FIFO" flushInterval ="60000" size ="512" readOnly ="true" />
自定义缓存 1 2 3 <cache type ="com.domain.something.MyCustomCache" /> <cache-ref namespace ="com.someone.application.data.SomeMapper" />
type指定的类实现org.mybatis.cache.Cache接口。这个接口是MyBatis框架中很多复杂的接口之一,但是简单给定它做什么就行。
1 2 3 4 5 6 7 8 9 10 public interface Cache { String getId () ; int getSize () ; void putObject (Object key, Object value) ; Object getObject (Object key) ; boolean hasKey (Object key) ; Object removeObject (Object key) ; void clear () ; ReadWriteLock getReadWriteLock () ; }