第一节 前言:先说一点没有用的东西,不想看直接跳过前面这段。其实吧,我所认为的程序员都是比较懒的,因为大部分时间都是比较清闲的,忙碌的时候大概就是新需求的开发,项目上线等等。而开发时间其实也很短,时间应该花在熟悉业务需求,这部分时间大概花了70%,剩下的就是开发时间了。为什么会花费那么多时间在熟悉业务需求,因为这个就得涉及到设计专家的事情了,大部分设计专家都是懂的业务的,但是他们不一定懂程序,客户说什么就是什么,最后坑了程序员。
第二节 技术描述:
言归正传:本章节的知识点就是Mybatis是如何开发持久层的。为了对比出mybatis的开发优势,我将分为两个部分展开叙述包括:1、传统开发持久层方式 2、mybatis代理开发持久层方式。
第三节 程序引导
开发之前,先看几个类:
SqlSessionFactoryBuilder:
1、通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory,将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。
2、在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。
SqlSessionFactory:
1、通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。
2、当mybatis和spring整合后,使用单例模式管理sqlSessionFactory。
SqlSession:
1、SqlSession是一个面向用户(程序员)的接口。
2、SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象)。
3、SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。解决方案:SqlSession最佳应用场合在方法体内,定义成局部变量使用。
一、传统开发持久层方式:
思路:程序员需要开发持久层接口(面向接口编程)和持久层实现类,以及需要在实现类中注入上面提到的SqlSessionFactory这个工厂类。
来一起看程序代码:
Dao接口内容如下:
package cn.mybatis.dao;
import cn.mybatis.pojo.User;
/**
*
* @ClassName: UserDao
* @Description: TODO(持久层接口)
* @author: Hanson
* @date: 2016年1月3日 下午1:26:11
*
*/
public interface UserDao {
// 根据ID查询用户信息
public User findUser(int id) throws Exception;
// 添加用户信息
public void insertUser(User user) throws Exception;
// 删除用户信息
public void deleteUser(int id) throws Exception;
// 更新用户信息
public void updateUser(User user) throws Exception;
}
Dao接口实现类如下:
package cn.mybatis.dao.impl;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import cn.mybatis.dao.UserDao;
import cn.mybatis.pojo.User;
/**
*
* @ClassName: UserDaoImpl
* @Description: TODO(Dao实现类)
* @author: Hanson
* @date: 2016年1月3日 下午1:28:51
*
*/
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
// 构造器注入
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUser(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUser", id);
sqlSession.close();
return user;
}
@Override
public void insertUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
sqlSession.close();
}
@Override
public void deleteUser(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUser", id);
sqlSession.commit();
sqlSession.close();
}
@Override
public void updateUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.update("test.updateUser", user);
sqlSession.commit();
sqlSession.close();
}
}
User.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="test">
<select id="findUser" parameterType="int" resultType="cn.mybatis.pojo.User">
SELECT
*
FROM USER WHERE id=#{id}
</select>
<insert id="insertUser" parameterType="cn.mybatis.pojo.User">
INSERT INTO
USER(username,sex,address,birthday)VALUES(#{username},#{sex},#{address},#{birthday})
</insert>
<delete id="deleteUser" parameterType="int">
delete from user where
id=#{value}
</delete>
<update id="updateUser" parameterType="cn.mybatis.pojo.User">
update user set
username=#{username},address=#{address}
where id=#{id}
</update>
</mapper>
mybatis全局配置文件SqlMapConfig.xml:
<?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>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc管理事务有mybatis -->
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml" />
</mappers>
</configuration>
测试代码:
package cn.mybatis.test;
import java.util.Date;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import cn.mybatis.dao.UserDao;
import cn.mybatis.dao.impl.UserDaoImpl;
import cn.mybatis.pojo.User;
public class UserDaoImplTest {
@Before
public void setUp() throws Exception {
/*
* SqlSessionFactory sqlFactory = new
* SqlSessionFactoryBuilder().build(Resources
* .getResourceAsStream("SqlMapConfig.xml"));
*/
}
@After
public void tearDown() throws Exception {
}
@Test
public void testInsertUser() throws Exception{
/**
* 构造器
*/
UserDao userDao = new UserDaoImpl(//
new SqlSessionFactoryBuilder()//
.build(Resources
.getResourceAsStream("SqlMapConfig.xml")));
User user = new User ();
//user.setId(26);主键自增长
user.setSex("1");
user.setUsername("cc");
user.setBirthday(new Date());
user.setAddress("NanJing");
userDao.insertUser(user);
}
@Test
public void testDeleteUser() throws Exception{
UserDao userDao = new UserDaoImpl(//
new SqlSessionFactoryBuilder()//
.build(Resources
.getResourceAsStream("SqlMapConfig.xml")));
userDao.deleteUser(35);
}
@Test
public void testUpdateUser() throws Exception{
UserDao userDao = new UserDaoImpl(//
new SqlSessionFactoryBuilder()//
.build(Resources
.getResourceAsStream("SqlMapConfig.xml")));
User user = new User();
user.setId(35);
user.setUsername("CC");
user.setAddress("南京");
userDao.updateUser(user);
}
@Test
public void testFindUser() throws Exception {
UserDao userDao = new UserDaoImpl(//
new SqlSessionFactoryBuilder()//
.build(Resources
.getResourceAsStream("SqlMapConfig.xml")));
User user = userDao.findUser(1);
System.out.println(user);
}
}
原始开发Dao方式总结:
1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
2、调用sqlsession方法时将statement的id硬编码了
3、调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
二、来看Mybatis代理方式开发持久层:
思路:使用代理开发得遵循代理开发的规范。
1、程序员还需要编写mapper.xml映射文件
2、程序员编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
代理开发规范很重要先来看下规范其实之前两张里面应该说过,这里就贴一下规范要求,还能记住的同学可以直接跳过了这个规范了
<!--
编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
规范:
1、namespace等于mapper接口地址
2、mapper.java接口中的方法名等于mapper.xml中statement的id一致
3、mapper.java接口中的方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
4、mapper.java接口中的方法的返回值类型和mapper.xml中statement的resultType指定的类型一致。
-->
以上规范主要是对以下代码进行统一:
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.insert("test.insertUser", user);
UserMapper接口等同于传统方式开发的Dao接口:
package cn.mybatis.mapper;
import java.util.List;
import cn.mybatis.pojo.User;
public interface UserMapper {
// 根据ID查询用户信息
public User findUserById(int id) throws Exception;
// 添加用户信息
public void insertUser(User user) throws Exception;
// 删除用户信息
public void deleteUser(int id) throws Exception;
// 根据用户名查询用户信息
public List<User> findUserByName(String username) throws Exception;
}
UserMapp.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接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
规范:
1、namespace等于mapper接口地址
2、mapper.java接口中的方法名等于mapper.xml中statement的id一致
3、mapper.java接口中的方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
4、mapper.java接口中的方法的返回值类型和mapper.xml中statement的resultType指定的类型一致。
-->
<mapper namespace="cn.mybatis.mapper.UserMapper">
<!-- id查找 -->
<select id="findUserById" parameterType="int" resultType="cn.mybatis.pojo.User">
select * from user where id =#{value}
</select>
<!-- 添加 -->
<insert id="insertUser" parameterType="cn.mybatis.pojo.User">
INSERT INTO USER(username,sex,address,birthday,id)VALUES(#{username},#{sex},#{address},#{birthday},#{id})
</insert>
<!-- 删除 -->
<delete id="deleteUser" parameterType="int">
delete from user where id=#{value}
</delete>
<!-- 更新 -->
<update id="updateUser" parameterType="cn.mybatis.pojo.User">
update user set
username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
<!-- name查找 -->
<select id="findUserByName" parameterType="string" resultType="cn.mybatis.pojo.User">
SELECT * FROM USER WHERE USERNAME LIKE '%${value}%'
</select>
</mapper>
几个重要的配置文件:
db.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql
log4j.properties:
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
SqlMapConfig.xml:
<?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="db.properties"></properties>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc管理事务有mybatis -->
<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>
</environments>
<!-- 加载映射文件 -->
<mappers>
<!-- 加载单个映射文件方式
<mapper resource="mapper/UserMapper.xml" />-->
<!-- mapper代理。。加载映射文件方式class要求mapper.xml和mapper.java的名字相同且在同一个目录下
<mapper class="cn.mybatis.mapper.UserMapper"/> -->
<!-- 批量加载mapper.xml文件方式。。推荐使用 -->
<package name="cn.mybatis.mapper"/>
</mappers>
</configuration>
测试代码:
package cn.mybatis.mapper;
import java.util.Date;
import java.util.List;
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.After;
import org.junit.Before;
import org.junit.Test;
import cn.mybatis.pojo.User;
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
sqlSessionFactory = new SqlSessionFactoryBuilder()//
.build(Resources//
.getResourceAsStream("SqlMapConfig.xml"));
}
@After
public void tearDown() throws Exception {
}
@Test
public void testFindUserById() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//生成代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(1);
System.out.println(user);
}
@Test
public void testFindUserByName() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findUserByName("小明");
for(User user : list){
System.out.println(user);
}
}
@Test
public void testInsertUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(100);
user.setAddress("无锡");
user.setSex("男");
user.setUsername("王小虎");
user.setBirthday(new Date());
userMapper.insertUser(user);
sqlSession.commit();
}
@Test
public void testDeleteUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.deleteUser(100);
}
}
写到这里就已经结束:
能够对比出代理方式和传统方式的区别,代理方式能够让程序元只专注于sql的编写。这样能够大大减轻开发者的工作。但是Mapper代理开发也是存在问题的,下面我也列出一些,欢迎大家补充:
1、代理对象内部调用selectOne或selectList?
如果mapper方法返回单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库。
如果mapper方法返回集合对象,代理对象内部通过selectList查询数据库。
2、mapper接口参数只能有一个是否影响系统开发?
mapper接口方法参数只能有一个,系统是否不利于扩展维护。
系统框架中,dao层的代码是被业务层公用的。
即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。
注意:持久层方法的参数可以包装类型、map。。。,service方法中建议不要使用包装类型(不利于业务层的可扩展)。
3、补充一个${}拼接会引起sql注入,个人的一点点小建议就是把常用的方法写成一个工具类,譬如校验是否为空,是否不是空,返回数据响应格式等等,这样方便将来开发不然每次都得重写。程序员嘛,重复的事情做一遍就好了。
解决方案如下代码:就是把需要模糊查询的字段用%拼接然后再使用#{}
package com.hanson.utils;
/**
*
* @ClassName: StringUtils
* @Description: TODO(字符串工具类)
* @author: Hanson
* @date: 2016年1月3日 下午2:08:24
*
*/
public class StringUtils {
/**
*
* @Title: isEmpty
* @Description: TODO (判断是否是空)
* @Author: Hanson
* @Create Date: 2016年1月3日 下午2:09:07
* @History: 2016年1月3日 下午2:09:07 Hanson Created.
*
* @param str
* @return
*
*/
public static boolean isEmpty(String str){
if(str==null||"".equals(str.trim())){
return true;
}else{
return false;
}
}
/**
*
* @Title: isNotEmpty
* @Description: TODO (判断是否不是空)
* @Author: Hanson
* @Create Date: 2016年1月3日 下午2:09:17
* @History: 2016年1月3日 下午2:09:17 Hanson Created.
*
* @param str
* @return
*
*/
public static boolean isNotEmpty(String str){
if((str!=null)&&!"".equals(str.trim())){
return true;
}else{
return false;
}
}
/**
*
* @Title: formatLike
* @Description: TODO (格式化模糊查询)
* @Author: Hanson
* @Create Date: 2016年1月3日 下午2:09:27
* @History: 2016年1月3日 下午2:09:27 Hanson Created.
*
* @param str
* @return
*
*/
public static String formatLike(String str){
if(isNotEmpty(str)){
return "%"+str+"%";
}else{
return null;
}
}
}
有什么疑问可以贴在下面。
下一章:输入映射,有时间会接着写的。
本文为慕课网作者原创,转载请标明【原文作者及本文链接地址】。侵权必究,谢谢合作!
热门评论
大神写的很好,可以把源码包放出来吗?
我先说下我的思路,我不知道你具体是怎么实现的。 首先根据findUserById能查询出一个User的话。 //查询出id为1的用户 User user = userMapper.findUserById(1); //接着更新这个用户的话 user.setName("新名字"); ... //需要注意的是 按照mapper.xml中是根据id去更新用户的,也就是要修改这个查询出来的用户。除了id这个字段不可以修改其他都是允许的。不然是不一定能够找到你给定id的用户
您好,我按照你的代码,在mapper中添加一个更新的方法, 如下: public void updateUser(User user) throws Exception; junit中调用时首先查询findUserById方法,查询出user,然后更新,但是发现查询方法可以查到,但是更新方法没有能够实现,请教大神告诉我原因,我试着重新创建sqlsession,但是也不好使。