首先看框架结构图:
如图所示:使用通过mybatis访问数据库首先需要一个SqlSessionFactory来创建SqlSession,而SqlSessionFactory的创建需要mybatis的配置文件,这个配置文件中配置了数据源、数据库事务等mybatis的运行环境以及映射文件(在后面整合了spring后数据源、事务、映射等重要信息基本都在spring中配置)。SqlSession可以理解为连接客户端与数据库的一个管道,通过statement语句进行对应的操作。
具体的框架配置可以网上查,这里只说关键配置,首先在我们的applicationContext-dao.xml中配置SqlSessionFactory:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<property name="typeAliasesPackage" value="com.liuyuan.pojo"/>
bean>
SqlSessionFactory中加载了配置文件,数据源,别名扫描。然后编写sql.xml中sql语句:
<select id="findUserById" resultType="user">
SELECT * FROM seckill.user where id =1;
select>
然后在mybatis配置文件中扫描sql.xml文件:
<mappers>
<mapper resource="sql/user.xml"/>
mappers>
最后测试代码:
@Test
public void findUserById(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-dao.xml",
"spring/applicationContext-service.xml");
SqlSessionFactory sessionFactory = (SqlSessionFactory) ctx.getBean("sqlSessionFactory");
SqlSession session = sessionFactory.openSession();
User user = session.selectOne("test.findUserById");
session.close();
System.out.println(user);
}
这里sql语句可以通过#{}占位符来接收输入参数:
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM seckill.user where id =#{id};
select>
也可以通过${}占位符来接收参数:
<select id="findUserByIdWithLike" parameterType="string" resultType="user">
SELECT * FROM seckill.user where username like '%${value}%';
select>
这两个的区别是第一个是固定占位符,第二个是字符串拼接占位符。
sql语句如下:
insert ignore into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address});
测试代码:
@Test
public void insertUser(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-dao.xml",
"spring/applicationContext-service.xml");
SqlSessionFactory sessionFactory = (SqlSessionFactory) ctx.getBean("sqlSessionFactory");
SqlSession session = sessionFactory.openSession();
User user = new User();
user.setUsername("hhh");
user.setAddress("123");
user.setBirthday(new Date());
user.setSex("1");
session.insert("test.insertUser", user);
System.out.println(user.getId());
session.commit();
session.close();
}
如果插入的时候要返回插入行数的话sql语句可以这样:
"insertUserSelectKey" parameterType="user">
"id" order="AFTER" resultType="java.lang.Integer">
Select LAST_INSERT_ID()
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address});
前面的测试中其实已经包含了简单参数和对象的输入映射,这里介绍包装类的输入映射,首先建立包装类:
public class QueryVo {
private User user;
//鑷畾涔夌敤鎴锋墿灞曠被
private UserCustom userCustom;
//浼犻�澶氫釜鐢ㄦ埛id
private List ids;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public UserCustom getUserCustom() {
return userCustom;
}
public void setUserCustom(UserCustom userCustom) {
this.userCustom = userCustom;
}
public List getIds() {
return ids;
}
public void setIds(List ids) {
this.ids = ids;
}
}
可以看到这个类里面组合了User,UserCustom和list,sql语句如下:
<select id="selectUserByVo" parameterType="QueryVo" resultType="user">
SELECT * FROM seckill.user where sex=#{userCustom.sex} and username like '%${userCustom.username}%';
select>
测试代码:
@Test
public void insertUserVo(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-dao.xml",
"spring/applicationContext-service.xml");
SqlSessionFactory sessionFactory = (SqlSessionFactory) ctx.getBean("sqlSessionFactory");
SqlSession session = sessionFactory.openSession();
QueryVo queryVo = new QueryVo();
UserCustom userCustom = new UserCustom();
userCustom.setUsername("源");
userCustom.setSex("1");
queryVo.setUserCustom(userCustom);
User user = session.selectOne("test.selectUserByVo", queryVo);
session.close();
System.out.println(users);
}
也是在上面的测试中已经通过resultType进行简单参数合类的输入映射了,可以看到,当数据库查询到的字段的名称和类的属性名称一致的时候会自动映射,否则不会。后面介绍通过resultType进行多表的映射以及resultMap进行一对多查询。
这里将where和if标签加入到sql语句中:
<select id="selectUserByDSQL" parameterType="QueryVo" resultType="user">
SELECT * FROM seckill.user
<where>
<if test="userCustom!=null">
<if test="userCustom.sex!=null and userCustom.sex!=''">
and sex=#{userCustom.sex}
if>
<if test="userCustom.username!=null and userCustom.username!=''">
and username like '%${userCustom.username}%'
if>
if>
where>
select>
然后测试即可。也可以将sql语句写成一个片段命名和调用:
"query_user_where">
<if test="userCustom!=null">
<if test="userCustom.sex!=null and userCustom.sex!=''">
and sex=#{userCustom.sex}
if>
<if test="userCustom.username!=null and userCustom.username!=''">
and username like '%${userCustom.username}%'
if>
if>
<select id="selectUserByDSQLBlock" parameterType="QueryVo" resultType="user">
SELECT * FROM seckill.user
<where>
"query_user_where">
where>
select>
最后是一个重要的标签foreach标签,用来遍历输入参数,可以向sql语句中传入列表进来:
<sql id="input_list_ids">
<if test="ids!=null">
<foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
id=#{user_id}
foreach>
if>
sql>
可以看到其中,collection的属性名和包装类中列表的名称一致,item表示列表成员,open是sql语句起始的话,close是结束的话,separator表示每个循环片段的中间分隔。然后调用即可:
<select id="selectUserByForeach" parameterType="QueryVo" resultType="user">
SELECT * FROM seckill.user
<where>
<include refid="input_list_ids">include>
where>
select>
首先通过resultType来接收数据的话需要将两个表查找的属性字段创建一个新的对象进行接收,类如下:
public class OrdersCustom extends Orders {
private String username;// 鐢ㄦ埛鍚嶇О
private String address;// 鐢ㄦ埛鍦板潃
private String sex;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
这样就包含了order类和User类的属性,然后写sql语句:
<select id="selectOdersByresultType" resultType="OrdersCustom">
SELECT
orders.*,
USER.username,
USER.sex,
USER.address
FROM
orders,
USER
WHERE orders.user_id = user.id
select>
最后测试即可:
public void selectOdersByresultType(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-dao.xml",
"spring/applicationContext-service.xml");
SqlSessionFactory sessionFactory = (SqlSessionFactory) ctx.getBean("sqlSessionFactory");
SqlSession session = sessionFactory.openSession();
List oders = session.selectList("test.selectOdersByresultType");
session.close();
System.out.println(oders);
}
然后使用resultType进行多表查询,首先编写resultMap:
<resultMap type="Orders" id="ordersResultMap">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<association property="user" javaType="com.liuyuan.pojo.User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
association>
resultMap>
可以看到id表示唯一键,result表示普通属性,然后采用association将查出来的字段属性映射到组合类中的属性去,property对应属性名称,javaType代表属性类型。而组合类的总id和result和外面的id和result的含义一致,然后写sql语句调用即可:
<select id="selectOdersByresultMap" resultMap="ordersResultMap">
SELECT
orders.*,
USER.username,
USER.sex,
USER.address
FROM
orders,
USER
WHERE orders.user_id = user.id
select>
最后利用resultMap实现一对多查询,这里主要是一个订单对应了一个User和多个订单信息。resultMap编写如下:
<resultMap type="Orders" id="userResultMapMul" extends="ordersResultMap">
<collection property="orderdetails" ofType="com.liuyuan.pojo.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
collection>
resultMap>
可以看到由于订单和用户属性和ordersResultMap中是一致的,这里可以直接继承ordersResultMap即可,然后编写集合的对应关系。集合的对应采用collection 实现,property代表集合属性名称,这里的属性类型一定要用ofType,然后后面的id和result的含义与上面的含义一致。
原始dao开发表示自己编写dao接口并且自己编写实现类,这里首先编写dao实现类:
public class UserDao {
private SqlSessionFactory sessionFactory;
public UserDao(SqlSessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public void insertUser() {
System.out.println("insert success!!");
}
public void findUserById(){
SqlSession session = sessionFactory.openSession();
User user = session.selectOne("test.findUserById");
session.close();
System.out.println(user);
}
}
由于SqlSessionFactory应该是单例的,所以这里可以通过构造方法将SqlSessionFactory 给传入,然后编写一个查找方法即可,对应sql.xml中的语句,然后编写测试类:
@Test
public void userDao(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-dao.xml",
"spring/applicationContext-service.xml");
SqlSessionFactory sessionFactory = (SqlSessionFactory) ctx.getBean("sqlSessionFactory");
UserDao userDao = new UserDao(sessionFactory);
userDao.findUserById();
}
mapper接口开发是mybatis框架提供的一种开发方法,不过需要满足几条规则,首先xml中namespace的名称要和接口的全限定名一致,sql的id名要和接口总的方法名一致,sql的输入参数和输出参数要和接口的输入参数合返回值一致,这里编写xml文件:
<mapper namespace="com.liuyuan.dao.UserMapper">
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM seckill.user where id =#{id};
select>
mapper>
再编写接口:
public interface UserMapper {
public User findUserById(int id);
}
然后扫描这个接口:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.liuyuan.dao"/>
bean>
最后测试:
public void userMapper(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-dao.xml",
"spring/applicationContext-service.xml");
UserMapper userMapper = (UserMapper) ctx.getBean("userMapper");
User user = userMapper.findUserById(1);
System.out.println(user);
}
首先将sql语句抽离到xml中,方便集中管理,并且减少代码的耦合度。然后提供了动态sql标签,灵活性比较强。也提供了resultmap标签,方便一对多查询。其次还提供了mapper接口,提高了开发效率。最后与Jdbc相比,开发更加简单,开发效率提高。与Hibernate相比,可以自己编写sql语句,灵活性更强,方便优化。缺点就是sql编写起来较为复杂,但对于单表的编写可以直接用逆向工程生成,sql比较依赖对应的数据库,移植性没那么好。