Mybatis入门
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
Mybatis基本配置
pom.xml
若不用maven构建项目可以直接导入jar包:mybatis-x.x.x.jar。
使用Maven:需将下面的dependency 代码置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
Configuration.xml
<configuration> <!--配置包的别名--> <typeAliases> <typeAlias alias="my.Student" type="com.seu.sun.model.Student"/> <!--<package name="com.seu.sun.model"/>--><!--也可以这样写包名--> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="" value=""/> </transactionManager> <dataSource type="UNPOOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="0000"/> </dataSource> </environment> </environments> <!--配置一个包含完整类路径的xml文件--> <mappers> <mapper resource="com/seu/sun/mybatisConfig/mapper/Student.xml"/> </mappers> </configuration>
<settings>
<setting name=”mapUnerscoreToCamelCasevalue=”true””>
<settings>
该设置将数据库中下划线命名(user_name)映射到Java驼峰命名(userName)。
Student.xml
<!DOCTYPE mapper PUBLIC "-//com.seu.sun.mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--对应接口的包名接口名(无需实现)使用接口的用法--> <mapper namespace="com.seu.sun.mybatisConfig.mapper.StudentMapper"> <resultMap type="my.Student" id="StudentsResult"> <id column="id" jdbcType="INTEGER" property="id"/> <result column="name" jdbcType="VARCHAR" property="name"/> <result column="age" jdbcType="INTEGER" property="age"/> <result column="hobby" jdbcType="VARCHAR" property="hobby"/> </resultMap> <select id="selectAll" resultMap="StudentsResult"> SELECT id,name,age,hobby FROM students </select> <select id="selectById" resultMap="StudentsResult"> SELECT id,name,age,hobby FROM students WHERE id=#{id} </select> <insert id="insert"> INSERT INTO students(name,age,hobby) VALUES (#{name},#{age},#{hobby}) </insert> <update id="updateById"> UPDATE students SET name = #{name},age=#{age} WHERE id=#{id} </update> <delete id="deleteById"> DELETE FROM students WHERE id=#{id} </delete> </mapper>
result标签
可以不配置resultMap,直接配置resultType。
<result>标签用于配置结果映射的方法,id为唯一标识,select标签填的resultMap即为该id,type为用于配置查询列所映射到的Java对象类型。extends选填,可以配置当前resultMap继承自其它resultMap,autoMapping选填,true或false,是否使用非映射字段(没有在resultMap中配置的字段)的自动映射功能。
该标签包括以下子标签:
<constructor>配置使用构造方法注入结果
<id>一个id结果,代表主键,<result>注入到java对象属性的普通结果。这两个标签包含以下属性:column数据库中得到的列名或列的别名,property映射到列结果的属性,javaType一个java类的完全限定名或别名(typeAlias配置),jdbcType列对应的数据库类型,typeHandler可以覆盖默认类型处理器。
<association>一个复杂的类型关联,许多结果将包装成这种类型
<collection>复杂类型的集合,实现一对多
<discriminator>根据结果值来决定使用哪个结果映射
<case>
select标签
<select>标签,id为命名空间中唯一标识符,可用来代表这条语句。resultMap为返回值的类型和映射关系,也可以配置resultType为java的某个类型。查询语句中#{id}是预编译参数的一种方式。
insert标签
<insert>标签,id同上。flushCache默认为true,任何时候只要语句被调用都会清空一级和二级缓存。timeout设置在抛出异常前,驱动程序等待数据库返回请求结果的秒数。statementType有STATEMENT、PREPARED、CALLABLE会分别使用Statement、PreparedStatement、CallableStatement,默认PREPARED。useGeneratedKeys默认false,设置true,Mybatis会使用JDBC的getGeneratedKeys方法来取出数据库内部生成的主键。KeyProperty获取主键值后将要赋值的属性名。对于指定的类型可以使用如:#{para,jdbcType=DATE}。
也可以使用子标签<selectKey>获取回写主键。
update标签
用法和insert比较类似。
delete标签
用法和insert比较类似。
DBAccess.java
从 XML 中构建 SqlSessionFactory
用于连接数据库,获得sqlSession会话
/** * 访问数据库类 */ public class DBAccess { public SqlSession getSqlSession() throws IOException { // 通过配置文件获取数据库连接信息 Reader reader = Resources.getResourceAsReader("com/seu/sun/mybatisConfig/Configuration.xml"); // 通过配置信息构建一个SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // 通过sqlSessionFactory打开一个数据库会话 SqlSession sqlSession = sqlSessionFactory.openSession(); return sqlSession; } }
纯XML实现方法
testCRUD.java
编写增删改查的各个方法,对应Student.xml文件中映射的方法。
/** * 实现方法有XML和接口方法 */ public class testCRUD { private static SqlSession sqlSession; public static void main(String[] args) throws IOException { DBAccess dbAccess = new DBAccess(); sqlSession=dbAccess.getSqlSession(); // testSelect(); // testSelectById(); // testInsert(); // testUpdateById(); // testDeleteById(); useInterface(); sqlSession.close(); } /** * 增删改查操作 * 传入的类型参数可以为基本类型(一个),JavaBean(一个) * 当参数为多个时,可以使用Map(需要自己建立对应Key-Value) *也可以使用@Param注解(接口的方法,下面介绍) * */ public static void testSelect(){ List<Student> list = new ArrayList<>();
//若冲突需要些写mapper的namespace list=sqlSession.selectList("selectAll"); for(Student s:list){ System.out.println(s.getName()+s.getHobby()); } } public static void testSelectById(){ // Student s = new Student(); // s.setId(6);//也可以传入Student对象,可以自动识别 Student s = sqlSession.selectOne("selectById",1); System.out.println(s.getName()+s.getHobby()); } public static void testInsert(){ Student s = new Student(); s.setAge(10);s.setHobby("跳舞");s.setName("小王"); sqlSession.insert("insert",s); sqlSession.commit(); } public static void testUpdateById(){ Student s = new Student(); s.setId(9); s.setAge(99);s.setHobby("没有");s.setName("小小"); //可以通过map对参数直接赋值 // Map<String,Object> map = new HashMap<>(); // map.put("name","xiaohua");map.put("id",9);map.put("age",20); sqlSession.update("updateById"); sqlSession.commit(); } public static void testDeleteById(){ sqlSession.delete("deleteById",10); sqlSession.commit(); }
接口配合XML实现
接口可以配合XML使用,也可以配合注解来使用。XML可以单独使用,注解必须在接口中使用。
写对应的接口,方法名即为student.xml中对应的方法id,返回类型必须和XML中配置的resultType或resultMap中type类型一致。
StudentMapper.java
接口和XML是通过将namespace的值设置为接口的全限定名称来进行关联的(当只使用XML不用接口时,namespace可以设置为任意不重复的名称)。
将接口中方法和XML中定义的SQL语句(标签中id的值等于方法名)关联到一起。接口方法是可以重载的,但XML中id不能重复,接口中所有同名方法会对应XML中同一个id的方法。最常见的用法就是同名方法的其中一个增加RowBound类型参数用于实现分页查询。
传入的类型参数可以为基本类型(一个),JavaBean(一个),当参数为多个时,可以使用Map(需要自己建立对应Key-Value)也可以使用@Param注解。给参数配置@Param注解后,MyBatis就会自动将参数封装成Map类型,将方法参数和SQL语句中预编译参数进行映射。
Mapper接口没有实现类直接被调用是因为MyBatis使用了动态代理技术。
/** * 参数直接写在方法声明中,多个参数用@Param注解 */ public interface StudentMapper { List<Student> selectAll(); Student selectById(int id); int insert(Student student); int updateById(@Param("id")int id,@Param("name")String name,@Param("age")int age); int deleteById(int id); }
testCRUD.java
public static void useInterface(){ StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); studentMapper.selectAll(); studentMapper.selectById(1); Student s = new Student(); s.setId(3); s.setName("小花"); s.setHobby("待定"); s.setAge(13); studentMapper.insert(s); studentMapper.deleteById(11); studentMapper.updateById(9,"呵呵",18); sqlSession.commit(); }
注解方式基本用法
Mybatis注解就是将SQL语句直接写在接口上。
优点:开发效率高
缺点:修改SQL都需要重新编译
student2.xml
<!--对应接口的包名+接口名(无需实现)--> <mapper namespace="com.seu.sun.mybatisConfig.mapper.Student2Mapper"> <resultMap type="my.Student" id="StudentsResult"> <id column="id" jdbcType="INTEGER" property="id"/> <result column="name" jdbcType="VARCHAR" property="name"/> <result column="age" jdbcType="INTEGER" property="age"/> <result column="hobby" jdbcType="VARCHAR" property="hobby"/> </resultMap> </mapper>
Student2Mapper.java
public interface Student2Mapper { @Select({"select id,name,age,hobby", "from students", "where id=#{id}"}) Student selectById(int id); @Select("select id,name,age,hobby from students") List<Student> selectAll(); @Insert("insert students (name,age,hobby) VALUES (#{name},#{age},#{hobby})") int insert(Student student); @Update("UPDATE students SET name = #{name},age=#{age} WHERE id=#{id}") int updateById(@Param("id")int id, @Param("name")String name, @Param("age")int age); @Delete("DELETE FROM students WHERE id=#{id}") int deleteById(int id); }
@Result注解
如:
@Results({
@Result(property = "id",column = "id",id = true),
@Result(property = "name",column = "name"),
@Result(property = "age",column = "age"),
@Result(property = "hobby",column = "hobby")
})
@Select注解
参数可以是字符串数组类型也可以是字符串类型。如上面的例子。
@Insert注解
该注解本身比较简单,但需要返回主键的值会稍微复杂。使用@Options(useGeneratedKeys=true,keyProperty = “id”)来设置这两个属性。若返回非自增主键,使用@SelectKey注解。
@Update注解和@Delete注解
使用方法较为简单,例子见上面。
@Provider注解
MyBatis还提供了4种Provider注解,分别是@SelectProvider、@InsertProvider、@UpdateProvider和#DeleteProvider实现增删改查的功能。
testCRUD.java
public static void useInterface2(){ Student2Mapper student2Mapper = sqlSession.getMapper(Student2Mapper.class); Student s = student2Mapper.selectById(1); System.out.println(s.getName()); for(Student stu:student2Mapper.selectAll()){ System.out.println(stu.getId()+stu.getName()); } s.setAge(100);s.setId(9); student2Mapper.insert(s); student2Mapper.deleteById(14); sqlSession.commit(); }
Mybatis动态SQL
if用法
在where语句中判断是否使用某个查询条件,if标签有一个必填属性test,判断条件property!=null适用于任何类型字段,而property!=’’仅适用于String类型字段,判断是否为空字符串。如:
<select id="selectAll" resultMap="StudentsResult"> SELECT id,name,age,hobby FROM students WHERE 1=1 <if test="name !=null and name != '' "> AND name LIKE concat('%',#{name},'%') </if> </select>
在update语句中判断是否更新某一字段,只更新变化的字段,需要注意每个if元素SQL语句后面都有逗号,还有一个就是where语句前面的id=#{id}。
<update id="updateByIdSelective"> UPDATE students SET <if test="name !=null and name != ''"> name = #{name}, </if> <if test="age !=null and age > 0"> age = #{age}, </if> <if test="hobby !=null and hobby != ''"> hobby = #{hobby}, </if> <if test="classAndGradeId !=null and classAndGradeId != ''"> classAndGradeId = #{classAndGradeId}, </if> id=#{id} WHERE id=#{id} </update>
在insert语句中判断是否插入某个字段值,如果参数不空,则使用传入的值,否则使用数据库默认值,原理同上。不加代码了。
choose用法
choose可以实现if...else...的功能,一个choose至少有一个when标签,0~1个otherwise标签。
<select id="selectByIdOrName" resultMap="StudentsResult"> SELECT id,name,age,hobby,classAndGradeId FROM students WHERE 1=1 <choose> <when test="name !=null and name != ''"> and name = #{name} </when> <when test="id !=null and id != ''"> and id = #{id} </when> <otherwise> and 1=2 </otherwise> </choose> </select>
where/set/trim用法
where
可以将上面的where语句换成where标签,免去了where 1=1,同时标签中也会自动去掉第一and或or。
<select id="selectByName2" resultMap="StudentsResult"> SELECT id,name,age,hobby,classAndGradeId FROM students <where> <if test="name !=null and name != '' "> AND name LIKE concat('%',#{name},'%') </if> </where> </select>
set
将上面set语句换成set标签,可以去掉最后的逗号,但是id=#{id}最好保留,如果set标签中没有内容则依然会报错。
<update id="updateByIdSelective2"> UPDATE students <set> <if test="name !=null and name != ''"> name = #{name}, </if> <if test="age !=null and age > 0"> age = #{age}, </if> <if test="hobby !=null and hobby != ''"> hobby = #{hobby}, </if> <if test="classAndGradeId !=null and classAndGradeId != ''"> classAndGradeId = #{classAndGradeId}, </if> id=#{id}, </set> <where> id=#{id} </where> </update>
trim
where和set标签都可以用trim标签来实现。where标签对应trim标签如下:
<trimprefix=”WHERE” prefixOverrides=”AND | OR ”>...</trim>
set对应trim标签如下:
<trimprefix=”SET” suffixOverrides=”,”>...</trim>
trim标签有如下属性,prefix会增加指定前缀,prefixOverrides去掉匹配的前缀,suffix增加指定后缀,suffixOverrides去掉匹配的后缀。
foreach用法
foreach包含以下属性:collection为要迭代循环的属性名。item迭代的每个变量名。index索引的属性名(数组时为索引值,map类型时为key)。open整个循环内容开头的字符串。close整个循环内容结尾的字符串。separator每次循环插入的分隔符。如:
<delete id="deleteByIdSet"> DELETE FROM students <where> id in <foreach collection="list" open="(" close=")" separator="," item="id" index="i"> #{id} </foreach> </where> </delete>
批量插入例子:
<insert id="insertList"> INSERT INTO students(id,name,age,hobby,classAndGradeId) VALUES <foreach collection="list" item="stu" separator=","> (#{stu.id},#{stu.name},#{stu.age},#{stu.hobby},#{stu.classAndGradeId}) </foreach> </insert>
需要注意的是collection属性为一个集合的时候可以使用默认的“list”,如果为多个或使用其他变量名需要使用@Param注解。
Mybatis高级查询
一对多配置
model
这里一个两个model:班级和学生,一个班级包含多名学生。
public class ClassAndGrade { private String id; private String description; private List<Student> studentList; }
public class Student { private String id; private String name; private int age; private String hobby; private String classAndGradeId; }
ClassAndGrade.xml
在栗子中,一对多映射,一个班级下还有一个学生list,在resultMap标签中使用collection标签,property使用模型中变量名称即studentList,resultMap选择映射学生表的结果,要加上命名空间全称。
在SELECT语句中,使用left joinon,注意使用别名,防止命名冲突,如id。修改别名的话,标签中column也要改成别名,如下面标红位置。
<!DOCTYPE mapper PUBLIC "-//com.seu.sun.mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--对应接口的包名+接口名(无需实现)--> <mapper namespace="com.seu.sun.mybatisConfig.mapper.ClassAndGradeMapper"> <resultMap type="com.seu.sun.model.ClassAndGrade" id="ClassAndGradeResult"> <id column="C_id" jdbcType="VARCHAR" property="id"/> <result column="description" jdbcType="VARCHAR" property="description"/> <!--一对多映射--> <collection property="studentList" resultMap="com.seu.sun.mybatisConfig.mapper.StudentMapper.StudentsResult"/> </resultMap> <select id="queryClassAndGradeList" resultMap="ClassAndGradeResult"> SELECT a.id C_id,a.description,b.id,b.name,b.hobby,b.age FROM classandgrade a left join students b ON a.id = b.classAndGradeId </select> </mapper>
记得在Configuration配置文件中添加新的xml文件的mapper标签。
testCRUD.java
public static void test1ToN(){ ClassAndGradeMapper classAndGradeMapper = sqlSession.getMapper(ClassAndGradeMapper.class); List<ClassAndGrade> list= classAndGradeMapper.queryClassAndGradeList(); for (ClassAndGrade c:list){ System.out.println(c.getId()+c.getDescription()); for(Student s:c.getStudentList()){ System.out.println(s.getId()+s.getName()+s.getAge()+s.getHobby()); } } }
Spring集成Mybatis
pom.xml依赖
除了Spring和mybatis本身需要的依赖,还需要在pom.xml中添加依赖:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency>
如遇到NoClassFound之类找不到类的错误,直接查找该类的依赖加上。。。
applicationContext.xml配置
<!--引入配置文件--> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:DBconfig.properties" /> </bean> <!--配置数据库连接池--> <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource"> <property name="driver" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--集成Mybatis配置--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--配置mybatis配置xml路径,除了数据源之外,对mybatis的各种配置仍然这样配置--> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/> <!--配置数据源,必选项--> <property name="dataSource" ref="dataSource"/> <!--扫描xml映射文件的路径--> <property name="mapperLocations"> <array> <value>classpath:mybatis/**/mapper/*.xml</value> </array> </property> <property name="typeAliasesPackage" value="com.seu.model"/> </bean> <!--配置MapperScannerConfigurer,自动扫描所有Mapper接口,使用时可以直接注入接口--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.seu.**.mapper"/> </bean>
配置完后,写mapper接口和mapper.xml映射文件,相当于DAO层,mapper接口直接使用@Repository注射为Bean。