mybatis的介绍
- mybatis 就是一个封装JDBC的持久层框架,它和hibernate都属于ORM框架,但是具体的来说bihernatea是一个完整的ORM框架,mybatis 是不完整的ORM框架
- Mybatis 让程序员只关注sql本身,而不需要关注如何连接、statement的创建等操作
- Mybatis 输入参数和输出结果进行映射.
mybatis文档
分析jdbc的问题
原生态的jdbc代码
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1、加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2、通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
//3、定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
//4、获取预处理statement
preparedStatement = connection.prepareStatement(sql);
//5、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//6、向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//7、遍历查询结果集
while(resultSet.next()){
User user
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//8、释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
问题总结
1、 在创建连接时,存在硬编码配置文件(全局配置文件)
2、 在执行statement时存在硬编码
配置文件(映射文件3、 频繁的开启和关闭数据库连接,会造成数据库性能下降。
数据库连接池(全局配置文件)
工作原理
pom.xml 文件
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
MyBatis配置文件
<?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>
<settings>
<!--
开启数据库列名下划线风格转换为驼峰命名法
例如 user_name -> userName
last_access_time -> lastAccessTime
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--配置别名-->
<typeAliases>
<typeAlias type="com.kaishengit.pojo.User" alias="User"/>
<typeAlias type="com.kaishengit.pojo.Employee" alias="Employee"/>
<typeAlias type="com.kaishengit.pojo.Dept" alias="Dept"/>
</typeAliases>
<!--配置数据库环境-->
<environments default="dev">
<environment id="dev">
<!--配置事务管理器-->
<transactionManager type="JDBC"/>
<!--配置数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db_22"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--加载Mapper.xml文件-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/EmployeeMapper.xml"/>
<mapper resource="mapper/DeptMapper.xml"/>
<mapper class="com.kaishengit.mapper.AccountMapper"/>
</mappers>
</configuration>
Mapper文件
<?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.kaishengit.mapper.UserMapper">
<cache/>
<!--
将select节点的useCache属性设置为false表示该查询结果不会放入到缓存中
-->
<select id="findById" useCache="false" parameterType="int" resultType="User">
SELECT * FROM t_user WHERE id = #{id}
</select>
<select id="findAll" resultType="User">
SELECT * FROM t_user
</select>
<select id="findByUserNameAndPassword" resultType="com.kaishengit.pojo.User">
SELECT * FROM t_user where user_name=#{name} and password=#{pwd}
</select>
</mapper>
- • namespace:命名空间,可以和同样完全限定名中的一个Mapper类对应;
- • id:该查询语句的唯一标示
- • parameterType:参数的类型
- • resultType:返回值类型,如果是自己定义的Entity,需要在config文件中配置别名
测试
public class MyBatisTestCase {
@Test
public void readXml() {
try {
//1. 读取classpath中的配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");
//2. 构建SqlSessionFactory对象
SqlSessionFactory sessionFactory =
new SqlSessionFactoryBuilder().build(reader);
//3. 创建SqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
//执行查询单个对象语句
User user = sqlSession.selectOne("com.kaishengit.mapper.UserMapper.findById",1);
System.out.println(user);
//4. 释放资源
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void findById() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSession();
User user = sqlSession.selectOne("com.kaishengit.mapper.UserMapper.findById",1);
System.out.println(user);
sqlSession.close();
}
@Test
public void save() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSession();
User user = new User();
user.setUserName("mybatis");
user.setPassword("109876");
sqlSession.insert("com.kaishengit.mapper.UserMapper.save",user);
//提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void update() {
SqlSession session = SqlSessionFactoryUtil.getSqlSession(true);
User user = session.selectOne("com.kaishengit.mapper.UserMapper.findById",58);
user.setUserName("Spring");
session.update("com.kaishengit.mapper.UserMapper.update",user);
session.close();
}
@Test
public void del() {
SqlSession session = SqlSessionFactoryUtil.getSqlSession(true);
session.delete("com.kaishengit.mapper.UserMapper.del",58);
session.close();
}
@Test
public void findAll() {
SqlSession session = SqlSessionFactoryUtil.getSqlSession();
List<User> userList = session.selectList("com.kaishengit.mapper.UserMapper.findAll");
for(User user : userList) {
System.out.println(user);
}
session.close();
}
}
事务
Mybatis默认在操作insert、update、delete时不会自动提交事务,需要通过SqlSession对象的commit()方法来提交事务,通过rollback()方法回滚事务
调用SqlSessionFactory对象的openSession(true)方法可以获取一个可以自动提交事务的SqlSession对象
使用Mapper接口
package com.wanggs.mapper;
public interface UserMapper {
User findById(Integer id);
void save(User user);
void update(User user);
void del(Integer id);
List<User> findAll();
}
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findById(10);
logger.debug("{}",user);
sqlSession.close();
多对一结果集映射
ONGL
一对多
<?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.kaishengit.mapper.DeptMapper">
<select id="findById" resultMap="deptMap">
SELECT
t_employee.id,empname,deptid,deptname
FROM
t_employee
INNER JOIN t_dept ON t_employee.deptid = t_dept.id
WHERE
t_dept.id = #{id}
</select>
<resultMap id="deptMap" type="Dept">
<id column="deptid" property="id"/>
<result column="deptname" property="deptname"/>
<collection property="employeeList" ofType="Employee">
<id column="id" property="id"/>
<result column="empname" property="empname"/>
<result column="deptid" property="deptid"/>
</collection>
</resultMap>
</mapper>
传入多个参数
测试
public class MyBaitsInterfaceTestCase {
private SqlSession sqlSession;
private UserMapper userMapper;
@Before
public void setup() {
sqlSession = SqlSessionFactoryUtil.getSqlSession();
userMapper = sqlSession.getMapper(UserMapper.class);
}
@After
public void close() {
sqlSession.close();
}
@Test
public void save() {
SqlSession session = SqlSessionFactoryUtil.getSqlSession(true);
User user = new User();
user.setUserName("Rose");
user.setPassword("123123");
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.save(user);
System.out.println(user.getId());
session.commit();
session.close();
}
@Test
public void findByParam() {
Map<String,Object> param = new HashMap<String,Object>();
//param.put("username","tom");
param.put("password","111");
User user = userMapper.findByParam(param);
System.out.println(user);
}
@Test
public void batchSave() {
List<User> userList = new ArrayList<User>();
userList.add(new User("王五","99887"));
userList.add(new User("咋喽","3344876"));
userList.add(new User("呵呵","998876"));
userMapper.batchSave(userList);
sqlSession.commit();
}
}
mapper
package com.kaishengit.mapper;
import com.kaishengit.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface UserMapper {
void save(User user);
User findByParam(Map<String,Object> param);
List<User> findByIds(List<Integer> idList);
void batchSave(List<User> userList);
}
sql
<?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.kaishengit.mapper.UserMapper">
<cache/>
<!--
将select节点的useCache属性设置为false表示该查询结果不会放入到缓存中
-->
<select id="findById" useCache="false" parameterType="int" resultType="User">
SELECT * FROM t_user WHERE id = #{id}
</select>
<select id="findByParam" resultType="com.kaishengit.pojo.User">
SELECT * FROM t_user
<trim prefix="where" prefixOverrides="and|or">
<if test="username != null and username != ''">
user_name = #{username}
</if>
<if test="password != null and password != ''">
and password= #{password}
</if>
</trim>
</select>
<select id="findByIds" resultType="com.kaishengit.pojo.User">
SELECT * FROM t_user where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<!--
useGeneratedKeys:true 表示使用自动增长的主键
keyProperty:id 将自动增长的主键赋值给parameterType对应类型的id属性
flushCache:false 不刷新缓存
-->
<insert id="save" parameterType="User"
useGeneratedKeys="true" keyProperty="id"
flushCache="false">
INSERT INTO t_user(user_name,password) VALUES (#{userName},#{password})
</insert>
<insert id="batchSave">
INSERT INTO t_user(user_name,password)
VALUES
<foreach collection="list" item="user" separator=",">
(#{user.userName},#{user.password})
</foreach>
</insert>
</mapper>
N+1问题
缓存
<mapper namespace="com.kaishengit.mapper.NodeMapper">
<cache/>
作用
• 映射语句文件中所有的select语句将被缓存
• 映射语句文件中的所有insert、update、delete语句会刷新缓存
• 缓存会使用least recentily used(LRU,最近很少使用的)算法来收回
• 根据时间间隔来刷新缓存,默认不刷新
• 缓存会存储列表集合或对象的1024个引用
• 缓存被视为read/write的缓存
注:pojo类必须是可序列化类
更改默认配置
<cache size="2048" readOnly="false" eviction="FIFO" flushInterval="60000"/>
• 存储2048个对象
• 不是只读缓存
• 缓存策略为FIFO
• 每隔60秒刷新一次缓存
• eviction回收策略
• LRU:最近最少使用的,移除长时间不被使用的对象(默认)
• FIFO:先进先出:按对象进入缓存的顺序来移除他们
• SOFT:软引用:移除基于垃圾回收器状态和软引用规则的对象
• WEAK:弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
• flushInterval(刷新间隔):可以被设置为任意的正整数
• size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认
值是 1024。
• readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此
这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会
慢一些,但是安全,因此默认是false。
一级缓存
测试一级缓存
二级缓存
逆向生成
1
2
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- Maven运行命令:mybatis-generator:generate -->
<!-- 连接数据库jar包的位置 -->
<classPathEntry location="D:\jar\mysql-connector-java-5.1.41.jar"/>
<!--
id: 自定义
targetRuntime : MyBatis3Simple 生成简单的CRUD语句
MyBatis3 生成带Example的CRUD语句
-->
<context id="myConfig" targetRuntime="MyBatis3">
<!--去掉自动产生的注释-->
<commentGenerator>
<!-- 是否去掉自动生成的注释 true是 false 否 -->
<property name="suppressAllComments" value="true"/>
<!-- 生成的注释是否带日期 true 不带 false 带 -->
<property name="suppressDate" value="true"/>
</commentGenerator>
<!-- 连接数据库的信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql:///ssm_crm?useSSL=false"
userId="root" password="rootroot"/>
<!-- POJO -->
<javaModelGenerator targetPackage="com.kaishengit.pojo" targetProject="src/main/java"/>
<!--XML映射文件-->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>
<!--Mapper接口-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.kaishengit.mapper"
targetProject="src/main/java"/>
<table tableName="t_user" domainObjectName="User" enableSelectByExample="true"/>
</context>
</generatorConfiguration>
运行插件
<build>
<!-- 插件的配置 -->
<plugins>
<!-- MBG插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
</plugin>
</plugins>
</build>