MyBatis深入
一、Dao的开发
1. 原始Dao
程序员写好Dao接口和Dao实现类
例子:
-
- Dao interface
package dao;
import pojo.User;
import java.util.List;
public interface UserDao {
public User findUserById(Integer id);
public List<User> findUserByUserName(String userName);
}
-
- DaoImpl
package dao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import pojo.User;
import java.util.List;
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
// 通过构造方法注入
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
public User findUserById(Integer id) {
// SQLSession是线程不安全的,所以最佳使用方法是放在方法体内
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
return user;
}
public List<User> findUserByUserName(String userName) {
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> user = sqlSession.selectList("test.findUserByUserName", userName);
return user;
}
}
-
- Test Dao
package dao;
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 org.junit.Before;
import pojo.User;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.*;
public class UserDaoImplTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void Init() throws IOException {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
public void testFindUserById(){
UserDao userDao= new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserById(1);
System.out.println(user);
}
}
原始Dao开发中存在以下问题:
Dao方法体存在重复代码:
通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
不利于维护:
调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码
2. Mapper动态代理
由程序员编写Mapper接口(类似Dao接口),由mybatis框架根据mapper接口自动创建动态代理对象,代理对象的方法类似Dao实现类的方法。
规范:
- Mapper.xml文件中的namespace和mapper接口的类路径相同;
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同;
- Mapper接口方法的输入参数类型和Mapper.xml文件中定义的每一个sql的parameterType的类型相同;
Mapper接口方法的输出参数类型和Mapper.xml文件中定义的每一个sql的resultType的类型相同。
-
- 映射文件—–Mapper.xml
Mapper.xml的编写方法和User.xml文件方法相同(User.xml文件参考链接http://blog.youkuaiyun.com/jun8148/article/details/79404553”>点我呀),
文件放于maven工程的resources目录下,与Mapper.java的包路径一样哦,后面有文件的图解哦!!!
<?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"> <mapper namespace="mapper.Mapper"> <!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int" resultType="pojo.User"> select * from user where id = #{id} </select> <!-- 自定义条件查询用户列表 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="pojo.User"> SELECT * FROM user WHERE username LIKE '%${value}%' </select> <!-- 添加用户 --> <insert id="insertUser" parameterType="pojo.User"> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO user(username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address}) </insert> </mapper>
-
- Mapper.java(interface)
注意事项:
- Mapper接口的方法名和Mappper.xml中定义的statement的id相同
- Mapper接口的方法的输入参数和Mapper.xml中定义的statement的parameterType类型相同
- Mapper接口的方法的输出参数和Mapper.xml中定义的statement的resultType类型相同
package mapper; import pojo.User; import java.util.List; public interface Mapper { /** * 根据用户id查询用户信息 * @param id id * @return User * @throws Exception */ public User findUserById(int id) throws Exception; /** * 查询用户列表 * @param username username * @return List<User> * @throws Exception */ public List<User> findUserByUsername(String username) throws Exception; /** * 添加用户信息 * @param user user * @throws Exception */ public void insertUser(User user)throws Exception; }
-
- 加载Mapper.xml
在SqlMapConfig.xml文件中添加
<mappers> <mapper resource="mapper/Mapper.xml"/> </mappers>
-
- 测试
package mapper; 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 org.junit.Before; import org.junit.Test; import pojo.User; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; import static org.junit.Assert.*; public class MapperTest { private SqlSessionFactory sqlSessionFactory; @Before public void init() throws IOException { String resouce = "SQLMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resouce); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void findUserById() throws Exception { // 获取session SqlSession session = sqlSessionFactory.openSession(); // 获取mapper接口的代理对象 Mapper userMapper = session.getMapper(Mapper.class); // 调用代理对象方法 User user = userMapper.findUserById(1); System.out.println(user); // 关闭session session.close(); } @Test public void findUserByUsername() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); Mapper userMapper = sqlSession.getMapper(Mapper.class); List<User> list = userMapper.findUserByUsername("张"); System.out.println(list.size()); } @Test public void insertUser() throws Exception { SqlSession session = sqlSessionFactory.openSession(); Mapper userMapper = session.getMapper(Mapper.class); // 要添加的数据 User user = new User(); user.setUsername("fuck"); user.setBirthday(new Date()); user.setSex("1"); user.setAddress("New York"); userMapper.insertUser(user); // 提交事物 session.commit(); session.close(); } }
二、SqlMapConfig.xml文件
- ### 映射器
<mappers>
<mapper resource=""/>
相对于类路径的资源
eg:<mapper resource="test/User.xml"/>
<mapper class=""/>
mapper接口类路径
这种要求mapper接口名称和mapper.xml文件名相同,且在同一目录内
eg:<mapper class="test1.test2.Mapper"/>
<package name=""/>
扫描并注册指定包下的所有mapper接口
这种要求mapper接口名称和mapper.xml文件名相同,且在同一目录内
eg:<package name="test.mapper"/>
扫描test.mapper包下的Mappper接口
三、附目录图
四、动态Sql
关键字:if、where、foreach、include
以代码为例哈
<!--比如我们要查询一个用户,既可以根据id查询,也可以根据名字查询,或者可以根据两者综合查询-->
<select id="findUser" parameterType="pojo.user" resultType="pojo.user">
SELECT * FROM user
<where>
<if test="id=null and id!=''">
and id=#{id}
</if>
<if test="username != null and username!=''">
and username LIKE '%${username}%'
</if>
</where>
</select>
<!--
if标签就是跟java的if选择结构一样的,test的内容就是条件,如果满足就执行,不满足就不执行
where标签就是自动为sql添加上where关键字,并且能自动去掉第一个and
-->
<!--根据多个id查询用户-->
<select id="findUserByIds" parameterType="vo.Test" resultType="pojo.User">
SELECT * FROM user
<if test="Ids!=null and Ids.size>0">
<foreach collection="Ids" open="where id in(" close=")" item="id" separator=", ">
#{id}
</foreach>
</if>
</select>
<!--
foreach功能就是依次遍历collection内的集合 open就是自动添加到之前的sql片段 close是结尾的 item是遍历到的每个对象存放的变量,separator是分隔符
比如我们跟据1,3,5,7,9查询上面的完整sql语句就是
select * from user where id in(1, 3, 5, 7, 9)
-->
<sql id="tests">
<if test="id!=null and id!=''">
and id=#{}
</if>
<if test="username!=null and username!=''">
and usernam='%${username}%'
</if>
</sql>
<select id="findUser" parameterType="pojo.User" resultType="pojo.User">
SELECT * FROM user
<where>
<include refid="tests"></include>
</where>
</select>
<!--
如果我们有重复的sql片段我们可以将其提出到<sql></sql>标签中 id是sql片段的唯一标识
当我们需要使用到这个片段的时候我们就可以利用<include></include>标签来引用它 refid就是sql片段的id
如果我们需要引用其他的namespace中的sql片段 就必须加上namespace名称
<include refid="namespace.sql"></include>
-->
<sql id="tests">
<if test="id!=null and id!=''">
and id=#{}
</if>
<if test="username!=null and username!=''">
and usernam='%${username}%'
</if>
</sql>
<select id="findUser" parameterType="pojo.User" resultType="pojo.User">
SELECT * FROM user
<where>
<include refid="tests"></include>
</where>
</select>
五、关联查询
假设在我们的user表的基础上添加一个cart表,里面有用户的购物信息
table of user:
id username sex address
table of cart:
id userId number note
class of User :
id(int) username(String) sex(String) address(String)
class of Cart:
id(int) userId(int) number(String) details(String)
一对一
方法一(用的是Mapper哦)
定义专门的一个类来作为输出类型
创建一个CartManager类继承Cart,然后把用户类的信息定义到该类中
// 位于pojo包内 public CartManager extends Cart{ private String username; private String address; // get set methods 没有检查过代码 随便敲的 有错自己改一下哦 }
查询(Mapper.xml)
<!--查询cart中的userId和user中的id相等的信息--> <select id="findCartList" resultType="pojo.CartManager"> SELECT cart.*, user.username, user.address FORM cart,user WHERE cart.userId = user.id </select>
Mapper.java(接口)
public List<CartManager> findCartList() throws Exception;
测试自己写咯
List<CartManager> list = sqlSession.getMapper(Mapper.class)
方法二
使用resultMap
Cart类有所改变,需要将User加入到Cart中
class of Cart: id(int) userId(int) number(String) details(String) user(User)
Mapper.xml
<resultMap id="findCartListResultMap" type="pojo.Cart"> <!-- column:表的对应的列 property:user对象中id属性--> <id column="id" property="id"/> <result column="id" property="userId"/> <result column="number" property="number"/> <result column="details" property="details"/> <!--property是Cart对象中的user属性,javaType是user属性对应的类型--> <association property="user" javaType="pojo.User"> <id column="user_id" property="id"/> <result column="username" property="username"/> <result column="address" property="address"/> </association> </resultMap> <select id="findCartList" resultMap="findCartListResultMap"> SELECT cart.*, user.username,user.address FROM WHERE cart.userId = user.id </select>
一对多
一个用户对应多个cart
更改User类
class of User id(int) username(String) sex(String) address(String) carts(List<Cart>)
Mapper.xml
<resultMap type="pojo.user" id="userCartManagerResultMap"> <!-- 用户信息映射 --> <id property="id" column="id"/> <result property="username" column="username"/> <result property="sex" column="sex"/> <result property="address" column="address"/> <!-- 一对多关联映射 --> <collection property="carts" ofType="pojo.Cart"> <id property="id" column="cid"/> <!--用户id已经在user对象中存在,此处可以不设置--> <!-- <result property="userId" column="id"/> --> <result property="number" column="number"/> <result property="details" column="details"/> </collection> <!-- collection部分定义了用户关联的信息。表示关联查询结果集 property="carts":关联查询的结果集存储在User对象的上哪个属性(carts)。 ofType="pojo.Cart":指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。 <id />及<result/>的意义同一对一查询。 --> </resultMap> <select id="userCartManager" resultMap="userCartManagerResultMap"> SELECT user.*, cart.id cid, cart.number, cart.createtime, cart.note FROM `user` u LEFT JOIN cart c ON u.id = c.userId </select>
Mapper接口
public List<User> userCartManager();
测试
Mapper mapper = session.getMapper(Mapper.class); List<User> users = mapper.userCartManager(); session.close();
六、MyBatis整合Spring
附一张目录图
利用maven创建一个工程
可以选择创建webapp或者quickstart
在pom.xml文件中添加依赖
<!--Spring依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!--mybatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!-- mysql驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> </dependency> <!--阿里的druid数据源jar包依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.18</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
编写jdbcConfig.properties文件
#jdbcConfig.properties jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/testSpring jdbc.username=root jdbc.password=1234 用来检测连接是否有效的sql,要求是一个查询语句。 如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会起作用。 validationQuery=SELECT 1 初始化连接大小 jdbc.initialSIze=0 连接池最大使用连接数量 jdbc.maxActive=20 连接池最小空闲 jdbc.minIdle=0 获取连接最大等待时间 jdbc.maxWait=60000
编写spring-mybatis.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--********************************************************************************************--> <!--设置扫描的包--> <context:component-scan base-package="com.lj.service"></context:component-scan> <!--********************************************************************************************--> <context:property-placeholder location="classpath:jdbcConfig.properties" ignore-unresolvable="true"/> <!-- 配置数据源 --> <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${jdbc.url}"/> <!-- 不必显示配置DriverClass Druid根据url前缀自动设别DriverClass <property name="driverClassName" value="${jdbc.driverClassName}"/> --> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 初始化连接大小 --> <property name="initialSize" value="${jdbc.initialSIze}"/> <!-- 连接池最大使用连接数量 --> <property name="maxActive" value="${jdbc.maxActive}"/> <!-- 连接池最小空闲 --> <property name="minIdle" value="${jdbc.minIdle}"/> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="${jdbc.maxWait}"/> <!-- 用来检测连接是否有效的sql,要求是一个查询语句。 如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会起作用。--> <property name="validationQuery" value="${validationQuery}"/> <!--申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值为true--> <property name="testOnBorrow" value="false"/> <!--归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值为false--> <property name="testOnReturn" value="false"/> <!-- 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis, 执行validationQuery检测连接是否有效。缺省值为false --> <property name="testWhileIdle" value="true"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接--> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置一个连接在池中最小生存的时间 --> <property name="minEvictableIdleTimeMillis" value="25200000"/> <!-- 打开removeAbandoned功能 --> <property name="removeAbandoned" value="true"/> <property name="removeAbandonedTimeout" value="1800"/> <!-- 关闭abanded连接时输出错误日志 --> <property name="logAbandoned" value="true"/> <!-- 监控数据库 属性类型是字符串,通过别名的方式配置扩展插件, 常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall --> <property name="filters" value="stat"/> </bean> <!-- myBatis文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--********************************************************************************************--> <!-- 配置数据库表对应的java实体类 --> <property name="typeAliasesPackage" value="com.lj.pojo"/> <!--********************************************************************************************--> <!--********************************************************************************************--> <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 --> <property name="mapperLocations" value="com/lj/mapper/*.xml"/> <!--********************************************************************************************--> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--********************************************************************************************--> <property name="basePackage" value="com.lj.mapper"/> <!--********************************************************************************************--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> </beans>
此处代码大都是copy的,不需要去记,知道怎么用就行了。 含有 的地方就是你需要更改成自己的东西的地方
编写UserMapper.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"> <mapper namespace="com.lj.mapper.UserMapper"> <select id="findUserById" parameterType="java.lang.Integer" resultType="User"> SELECT * FROM user WHERE id=#{id} </select> </mapper>
UserMapper接口
package com.lj.mapper; import com.lj.pojo.User; public interface UserMapper { public User findUserById(int id); }
User.java
package com.lj.pojo; public class User { private int id; private String userName; private String password; // 自己补充完整 记得有无参构造方法哦 }
UserService
package com.lj.service; import com.lj.pojo.User; public interface UserService { User getUserById(int id); }
UserServiceImpl
package com.lj.service.impl; import com.lj.mapper.UserMapper; import com.lj.pojo.User; import com.lj.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; public User getUserById(int id) { User user = userMapper.findUserById(id); return user; } }
test
import com.lj.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.junit.Assert.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring-mybatis.xml"}) public class UserTest { @Autowired private UserService userService; @Test public void testFindUserById(){ User user = userService.getUserById(1); System.out.println(user); } }