mybatis专栏 https://blog.csdn.net/worn_xiao/category_6530299.html?spm=1001.2014.3001.5482
一什么是Mybatis:
Mybatis是一个优秀的持久层框架。它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身。而不需要花费精力去注册驱动,创建连接connetion,创建statement,手动设置参数,结果集检索jdbc复杂的过程代码等复杂的过程代码。
Mybatis通过xml或注解的statement,Preparestatement,CallableStatement配置起来,并通过java对象和statement中的生成最终执行的sql语句,最后由mybaties框架执行sql并返回最终对象.
二 Mybatie,jdbc,hibernate的对比
2.1 Jdbc
public static void main(String[] args) {
Connectionconnection = null;
PreparedStatementpreparedStatement = null;
ResultSetresultSet = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
String sql = "select * from user whereusername = ?";
preparedStatement= connection.prepareStatement(sql);
preparedStatement.setString(1,"王五");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
}catch (Exception e) {
e.printStackTrace();
}finally{
if(resultSet!=null){
try {
resultSet.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Jdbc存在的问题与解决方案
一:频繁的开启或者关闭数据库连接,影响数据库性能。
二:代码中存在硬编码,数据库硬编码和SQL执行部门的硬编码。
解决上面的问题:通过xml的方式来配置。
2.2 Mybaties的技术特点:
1. 可以直接编写sql,并且对sql进行性能优化。
2. 学习门槛低,成本低,只要会sql就很快能够上手。
3. 直接编写sql,灵活性好,易于维护。
4. 对数据库无关性的支持不太好,如果数据库发生变更,要写多套代码。
5. 需要编写结果映射
2.3 Hibernate
1标准的orm框架,程序员不需要编写sql语句。
2 具有良好的数据库无关性,数据库发生改变的话无需变更配置,但是如果植入了sql的话,数据库变更就要进行变更。
3 学习门槛高,在or映射的时候既要考虑性能,也要权衡映射关系。
4 不能自主进行sql映射的优化。
Mybaties适合的场景:业务灵活多变的业务系统。
Hibernate适合的场景:业务固定的系统, 比如OA系统与CRM系统。
三 Mybaties工作原理图
1 mybaties配置文件:(mybaties全局配置文件与映射文件)
全局配置文件配置了数据源,事务等信息,映射文件配置了SQL执行相关的信息。
2 mybaties通过读取配置文件,构造出sqlsessionFactory,即构造出会话工厂。
3 通过sqlsessionFactory来创建会话sqlsession,并且通过sqlsession来直接连接数据库
4 sqlsession本身不能直接连接数据库,它是通过executor执行器来操作数据库的,executor本身有两个执行器,一个是普通的执行器,一个是默认的执行器。这个过程是同过一个mappper代理来完成的。
5 executor把要执行的sql信息封装到了一个底层mappedStatement对象中,该对象包括sql语句,输入参数映射信息,输出结果的映射信息。
四 全局配置文件常见配置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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<typeAliases>
<!-- 单个定义别名 -->
<typeAlias type="cn.itcast.mybatis.po.User" alias="user"/>
<!-- 批量定义别名(推荐) -->
<!-- [name]:指定批量定义别名的类包,别名为类名(首字母大小写都可) -->
<package name="cn.itcast.mybatis.po"/>
</typeAliases>
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
五 mybaties模拟基本的增删改查
5.1 模拟需求: 根据用户id查询用户信息
在User.xml中添加以下代码:
<mapper namespace="test">
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
SELECT * FROMUSER WHERE id = #{id}
</select>
</mapper>
Namespace:命名空间,它的作用是对SQL进行分类化管理,是SQL的隔离标识,如果是使用mapper代理做开发它有很大的作用。
Id:statement的id,命名空间内标签的唯一标识。
resultType:查询出单条结果集,所对应的java类型。
#{}: 标识一个占位符?
#{id}:表示该占位符待接收参数的名称为id,简单类型#{}里面的参数名称可以是任意定义的。
5.2 模拟需求: 根据用户名称模糊查询用户信息
User.xml中配置如下
<mapper namespace="test">
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
SELECT * FROMUSER WHERE id = #{id}
</select>
</mapper>
${} 表示拼接sql字符串。
${value} 表示要拼接的是简单类型的参数。
1:如果参数为简单类型时,里面的参数必须为value
2:${} 会引起sql注入,一般不推荐使用,但是有些场景必须使用${} 比如order by ${column}
5.3 模拟需求: 添加用户信息
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
INSERT INTOUSER(username,sex,birthday,address) VALUES (#{username},#{sex},#{birthday},#{address})
</insert>
#{} 表示占位符,就相当于原生sql预编译时的?,最终将会由executor进行调用jdbc的欲编译语句进行替换。
5.4 需求模拟: 删除用户
<delete id="deleteUser" parameterType="int">
DELETE FROM USERWHERE id= #{id}
</delete>
5.5 需求模拟:修改用户
<delete id="deleteUser" parameterType="int">
DELETE FROM USERWHERE id= #{id}
</delete>
六 mybaties主键生策略
6.1 自增主键的生成策略,以mysql为例来描述主键生成策略。
insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,sex,birthday,address) VALUES (#{username},#{sex},#{birthday},#{address})
</insert>
SelectKey标签:通过selectkey查询来生成主键。
KeyProperty:指定存放生成主键的属性。
resultType 主键所对应的java类型
order 制定该查询与句相对于insert语句的执行顺序,如果是自增主键应该在插入之前先执行,生成id,如果是主键回调,则应该放在插入之后执行。
6.2 主键生成策略之uuid()
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" resultType="string"order="BEFORE">
SELECT UUID()
</selectKey>
INSERT INTOUSER(id,username,sex,birthday,address) VALUES (#{id},#{username},#{sex},#{birthday},#{address})
</insert>
再插入之前先调用mysql的函数生成一个唯一的UUID,UUID生成的字符串是35位的。
6.3主键返回之oracle序列:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" resultType="int"order="BEFORE">
SELECT user_seq.nextval()FROM dual
</selectKey>
INSERT INTOUSER(id,username,sex,birthday,address) VALUES (#{id},#{username},#{sex},#{birthday},#{address})
</insert>
七 关键点总结
parameterType指定输入参数的java类型,可以填写别名或Java类的全限定名。
resultType指定输出结果的java类型,可以填写别名或Java类的全限定名。
#{}和${}
#{}:相当于预处理中的占位符?。
#{}里面的参数表示接收java输入参数的名称。
#{}可以接受HashMap、简单类型、POJO类型的参数。
当接受简单类型的参数时,#{}里面可以是value,也可以是其他。
#{}可以防止SQL注入。
${}:相当于拼接SQL串,对传入的值不做任何解释的原样输出。
${}会引起SQL注入,所以要谨慎使用。
${}可以接受HashMap、简单类型、POJO类型的参数。
当接受简单类型的参数时,${}里面只能是value。
八 Mybaties持久层开发方式
7.1 方式一:dao方式开发
操作步骤:
1 编写实体类
2 编写实体类xml映射文件
3 配置全局xml文件
4 加载全局文件,构建sqlsessionfactory
5 通过sqlsessionfactory创建sqlsession
6 通过sqlsession调用远程的select等方法
开发dao接口:
public interface UserDao {
public User findUserById(int id);
public List<User> findUsersByName(String username);
public void insertUser(User user);
}
SqlSessionFactoryBuilder
它的作用只是通过配置文件创建SqlSessionFactory,所以只要创建出SqlSessionFactory,它就可以销毁了。所以说,它的生命周期是在方法之内。
SqlSessionFactory
它的作用是创建SqlSession的工厂,工厂一旦创建,除非应用停掉,不要销毁。
所以说它的生命周期是在应用范围内。这里可以通过单例模式来管理它。
在mybatis整合spring之后,最好的处理方式是把SqlSessionFactory交由spring来做单例管理。
SqlSession
SqlSession是一个面向用户(程序员)的接口,它的默认实现是DefaultSqlSession。
Mybatis是通过SqlSession来操作数据库的。SqlSession中不仅包含要处理的SQL信息,还包括一些数据信息,所以说它是线程不安全的,因此它最佳的生命周期范围是在方法体之内。
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession.selectOne("test.findUserById", id);
}
@Override
public List<User> findUsersByName(String username) {
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession.selectList("test.findUsersByName", username);
}
@Override
public void insertUser(User user) {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("test.insertUser", user);
}
}
测试方法:
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserById(1);
System.out.println(user);
}
@Test
public void testFindUsersByName() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
List<User> list = userDao.findUsersByName("小明");
System.out.println(list);
}
@Test
public void testInsertUser() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = new User();
user.setUsername("东哥3");
user.setAddress("清河宝盛西里3");
userDao.insertUser(user);
System.out.println(user.getId());
}
}
Dao开发方式存在的一些问题:
1 存在一些模板代码比如创建sqlsessionfactory,创建sqlsession,关闭sqlsession。
2 存在一些硬编码,比如statement,参数之类的。
7.2 方式二 mapper方式开发
Mapper的开发规范:
1 mapper接口的全限定名要与xml配置文件中的namespace相同。
2 mapper接口中的方法名称要和statement中的id一致。
3 mapper接口中的参数只能有一个,且类型要与statement中是parametertype的类型一致。
4 mapper接口的返回值类型要和statement中的resulttype与resultmap一致。
操作:
根据需求创建po类
编写全局配置文件
根据需求编写映射文件
加载映射文件
编写mapper接口
编写测试代码
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">
<!-- namespace:此时用mapper代理方式,它的值必须等于对应mapper接口的全限定名 -->
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 根据用户ID,查询用户信息 -->
<!--
[id]:statement的id,要求在命名空间内唯一
[parameterType]:入参的java类型,可是是简单类型、POJO、HashMap
[resultType]:查询出的单条结果集对应的java类型
[#{}]: 表示一个占位符?
[#{id}]:表示该占位符待接收参数的名称为id。注意:如果参数为简单类型时,#{}里面的参数名称可以是任意定义
-->
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
<!-- 根据用户名称模糊查询用户信息列表 -->
<!--
[${}]:表示拼接SQL字符串,即不加解释的原样输出
[${value}]:表示要拼接的是简单类型参数。
1、如果参数为简单类型时,${}里面的参数名称必须为value
2、${}会引起SQL注入,一般情况下不推荐使用。但是有些场景必须使用${},比如order by ${colname}
-->
<select id="findUsersByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE '%${value}%'
</select>
<!-- 添加用户之自增主键返回(selectKey方式) -->
<!—
[selectKey标签]:通过select查询来生成主键
[keyProperty]:指定存放生成主键的属性
[resultType]:生成主键所对应的Java类型
[order]:指定该查询主键SQL语句的执行顺序,相对于insert语句,此时选用AFTER
[last_insert_id]:MySQL的函数,要配合insert语句一起使用-->
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,sex,birthday,address) VALUES (#{username},#{sex},#{birthday},#{address})
</insert>
</mapper>
在全局配置文件中配置mapper的映射
<mappers>
<mapper resource="sqlmap/User.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
编写mapper接口
publicinterface UserMapper {
publicUser findUserById(int id);
publicList<User> findUsersByName(String username);
publicvoid insertUser(User user);
}
测试代码:
public class UserMapperTest {
// 声明全局的SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void testFindUsersByName() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findUsersByName("小明");
System.out.println(list);
sqlSession.close();
}
@Test
public void testInsertUser() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("东哥4");
user.setAddress("清河宝盛西里4");
userMapper.insertUser(user);
System.out.println(user.getId());
sqlSession.commit();
sqlSession.close();
}
}