通过这篇文章主要是掌握Mybatis在开发中的各种使用技巧
Mybatis有中文的官网,整体内容不算难,这或许是最好的学习资料:mybatis – MyBatis 3 | 动态 SQL
使用JDBC进行开发最大的缺点在于代码量冗余,他们需要对异常进行正确的捕获与处理,并且要关注资源的释放。JDBC 的执行步骤
在使用JDBC驱动的是如果我们使用的是高版本驱动,在注册的时候如果是注册的这个类:
Class.forName("com.mysql.jdbc.Driver");
com.mysql.jdbc.Driver这个类已经被标记为弃用所以使用这个类进行注册时会有红色的提示,这时候我们可以将这个类换成:
com.mysql.cj.jdbc.Driver
下面是用JDBC进行数据库操作的代码:
public static void main(String[] args) throws Throwable {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try{
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//根据驱动获取Connection
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/girls","root","123456");
//用Connection获得Statement
statement = connection.createStatement();
//利用Statement执行对象的sql
resultSet = statement.executeQuery("select id,name,sex from beauty");
//遍历查询结果
while (resultSet.next()){
System.out.println(String.format("美女姓名:%s,美女主键:%d",resultSet.getString("name"),
resultSet.getInt("id")));
}
}catch (Exception e){
System.out.println("数据库连接错误:"+e.getMessage());
}finally {
close(connection,statement,resultSet);
}
}
使用JDBC连接数据库的时候我们要关闭的资源不只是Connection还有其他的一些资源,这是需要注意的:
private static void close(Connection connection,Statement statement,ResultSet resultSet){
try {
if (resultSet!=null && !resultSet.isClosed()){
resultSet.close();
}
}catch (Exception e){
System.out.println("ResultSet关闭错误:"+e.getMessage());
}
try {
if (statement!=null && !statement.isClosed()){
statement.close();
}
}catch (Exception e){
System.out.println("Statement关闭错误:"+e.getMessage());
}
try {
if (connection!=null && !connection.isClosed()){
connection.close();
}
}catch (Exception e){
System.out.println("Connection关闭错误:"+e.getMessage());
}
}
关于数据库连接资源关闭函数在看老师写的时候还在想,为什么不把这些if语句放到一个try中呢,这样不是看着更简洁一些吗?最后才发现将每一个资源的释放放在一个异常处理块中可以保证在前面资源关闭出错的时候,不影响到后面的资源关闭!
Hibernate是在08/09年很火的一个框架,相对于JDBC他可以将结果集的映射关系配置为xml文件,同时将我们对数据库的操作封装为一个Session对象,我们对数据库资源、异常的管理只需要聚焦于这个Session对象即可。
但是Hibernate退出历史舞台也是有其原因的,他对多表联查的支持不好,需要我们进行自己写SQL语句并且对结果集进行映射处理。不支持动态sql,并且在做更新操作的时候需要对表中所有字段进行更新操作。Hibernate不能够有效的支持存储过程,自身生成的HQL性能较差不能够进行优化。
Hibernate使用需要两种配置文件——Hibernate的配置文件、具体POJO对应的结果集映射文件:
hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driverproperty>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/girlsproperty>
<property name="hibernate.connection.username">rootproperty>
<property name="hibernate.connection.password">123456property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialectproperty>
<property name="hibernate.show_sql">trueproperty>
<property name="hibernate.format_sql">trueproperty>
<mapping resource="Beauty.hbm.xml" />
session-factory>
hibernate-configuration>
Beauty.hbm.xml:
<hibernate-mapping>
<class name="com.example.mybatis.domain.Beauty" table="beauty">
<id name="id" column="id">
<generator class="native"/>
id>
<property name="name" column="name"/>
<property name="sex" column="sex"/>
<property name="borndate" column="borndate"/>
<property name="phone" column="phone"/>
<property name="blob" column="blob"/>
<property name="boyfriend_id" column="boyfriend_id"/>
class>
hibernate-mapping>
Hibernate测试类:
Hibernate使用的时候主要的步骤包括:
public class HibernateTest {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
//这个SessionFactory我们可以封装成为一个全局的对象
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = null;
try{
session = sessionFactory.openSession();
Beauty beauty = session.get(Beauty.class,2L);
System.out.println(beauty);
}catch (Exception e){
System.out.println("Hibernate数据库查询错误:"+e.getMessage());
}finally {
//关闭数据库连接操作
if (session!=null){
session.close();
}
if (sessionFactory!=null){
sessionFactory.close();
}
}
}
}
Mybatis相对于Hibernate,他的支持动态SQL,并且由于SQL是我们自己写的所以我们可以对SQL进行优化,并且在Mybatis中支持存储过程。
Mybatis的使用也需要两种配置文件,一个是Mybatis的相关环境配置文件,以及映射相关的配置文件,不过在Mybatis的映射配置文件中,我们配置的核心映射是SQL语句与查询接口的一个映射关系,结果集的映射
mybatis-config.xml文件:
<configuration>
<properties resource="mybatisMapper/jdbc.properties" />
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<package name="com.example.mybatis.domain"/>
typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<databaseIdProvider type="DB_VENDOR"/>
<mappers>
<mapper resource="mybatisMapper/BeautyMapper.xml"/>
mappers>
configuration>
BeautyMapper.xml文件:
<mapper namespace="com.example.mybatis.mybatis.BeautyMapper">
<cache/>
<sql id="beautyAllColumn">
id,name,sex,borndate,phone,photo,boyfriend_id
sql>
<select id="getBeautyById" parameterType="int" resultType="Beauty">
select
<include refid="beautyAllColumn"/>
from
beauty
where
id = #{id}
select>
mapper>
Mybatis想要执行还需要一个与xml想对应的接口:
public interface BeautyMapper {
Beauty getBeautyById(@Param("id") int id);
}
由于项目中没有整合任何的第三方框架所以我们还需要实现一个对sqlSession进行管理的工具类,这一部分在项目中接触到的比较少所以要着重记忆一下
public class MybatisSqlSessionFactory {
private static SqlSessionFactory sqlSessionFactory = null;
public static SqlSession openSqlSession(){
if (sqlSessionFactory==null){
init();
}
return sqlSessionFactory.openSession();
}
private static SqlSessionFactory init(){
InputStream inputStream = null;
try{
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
System.out.println("Mybatis配置文件读取错误:"+e.getMessage());;
}
//双重锁保证并发情况下的安全性
synchronized (MybatisSqlSessionFactory.class){
if (sqlSessionFactory==null){
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
return sqlSessionFactory;
}
}
}
在Mybatis中我们对数据库的操作也是以Session为单位进行的,这一点与Hibernate是相似的,这不继承其他框架的时候使用Mybatyis进行操作的时候也是需要考虑资源的释放的;
@Test
public void test01(){
SqlSession sqlSession = null;
try{
sqlSession = MybatisSqlSessionFactory.openSqlSession();
//框架根据我们提供的接口的Class文件生成动态代理,我们的sql语句应该就是在这个动态代理的过程中
//生成并执行的
BeautyMapper beautyMapper = sqlSession.getMapper(BeautyMapper.class);
Beauty beauty = beautyMapper.getBeautyById(2);
System.out.println(beauty.toString());
}catch (Exception e){
System.out.println("Mybatis查询数据库错误:"+e.getMessage());
}finally {
if (sqlSession!=null){
sqlSession.close();
}
}
}
Mybatis在使用的过程中需要注意的一些内容:
1、Mybatis的配置文件如何被系统发现并起作用
2、Mybatis的映射文件如何与Mybatis的配置文件如何关联起来
3、Mybatis的接口如何与Mybatis的映射文件如何关联起来
如果实体的名称与数据库字段的名称没有完全一致相对应,要向实现结果集的映射需要通过开启驼峰自动匹配或是自己写对应的映射关系:
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
第一个配置的是自动映射的行为,他有三个值,其中PARTIAL是默认值,NONE是不映射,默认的是不管嵌套查询的,FULL是也关注嵌套查询的。
在使用自定义映射规则的时候需要注意ResultType与ResultMap的一个区别,不然的话这里容易出错
<resultMap id="userResultMap" type="vo.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>
Mybatis入参的方式有:简单类型、注解、Map、实体类等,他们在接口的参数设置上好理解,区别主要是在xml的配置文件中,一个核心的思想就是xml文件中可以拿到参数,而这些参数的组织与类型就是上面我们说的哪几中方式:
MessageVO getMessageVOById(Long id);
<select id="getMessageVOById" parameterType="long" resultMap="messageVoReusltMap">
select
<include refid="allColumns"/>
from
tb_message
where
id = #{id}
select>
public interface BeautyMapper {
//#{}中对应的是name
Beauty getBeautyById(int id,@Param("name") String name1);
}
public interface BeautyMapper {
Beauty getBeautyById(Map<String,Object> map);
}
selectMap.put("id",2);
selectMap.put("name","苍老师");
Beauty beauty = beautyMapper.getBeautyById(selectMap);
总之一句话,Mybatis并没有那么憨批,只要指明SQL语句与参数值的一个映射关系即可
#:采用的是预编译的方式构建SQL,安全
select id,name,sex,borndate,phone,photo,boyfriend_id from beauty where id = ?
$: 采用的是值传递的方式构建sql语句,与因为他是将参数直接拼接进了SQL,这样情况会造成SQL注入的安全问题
select id,name,sex,borndate,phone,photo,boyfriend_id from beauty where id = 2 and name = ‘苍老师’
当我们尝试注入的时候,发现#的方法没有查询出数据,而$的方式查询出所有数据
==> Preparing: select id,name,sex,borndate,phone,photo,boyfriend_id from beauty where id = ? and name = ?
==> Parameters: 2(Integer), ‘苍老师’ or 1 (String)
主键的回写还是调用了id的set方法
<insert id="insertBeauty" parameterType="Beauty" keyProperty="id" useGeneratedKeys="true">
insert into beauty(<include refid="selectBeautyAllColumn"/>)
values(#{name},#{sex},#{borndate},#{phone},#{blob},#{boyfriend_id})
insert>
在插入的时候发现数据库中并没有我们刚刚插入的数据,这是因为没有事务没有提交,
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1de24cc]
需要在insert之后进行一个commit()
sqlSession = MybatisSqlSessionFactory.openSqlSession();
//框架根据我们提供的接口的Class文件生成动态代理,我们的sql语句应该就是在这个动态代理的过程中
//生成并执行的
BeautyMapper beautyMapper = sqlSession.getMapper(BeautyMapper.class);
Beauty beauty = new Beauty();
beauty.setName("Liwudi");
beauty.setSex("男");
beauty.setPhone("32442134523");
beautyMapper.insertBeauty(beauty);
sqlSession.commit();
如果想过要让他默认提交可以在生成SqlSession的时候设置autoCommit为true(MybatisSqlSessionFactory中的方法):
public static SqlSession openSqlSession(){
if (sqlSessionFactory==null){
init();
}
return sqlSessionFactory.openSession(true);
}
返回是可以使用Map,这时候Map的key采用的是数据库表中的列名而非是实体中的名称,这样的使用情况在我们实际的开中是较少的
概念中常见的有两种:一对一(Association)、一对多(Collection)两种
在进行联表查询的时候,可以理解为以一张表为主,把主表的信息查询完了只有再调用另一个表的查询接口进行查询,在代码实践的过程我们主要做的是指明映射关系
<resultMap id="messageAndDetailReusltMap" type="vo.Message">
<id column="id" property="id"/>
<result column="msg_id" property="msgId"/>
<result column="status" property="status"/>
<result column="content" property="content"/>
<result column="deleted" property="deleted"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<association property="messageDetail" column="msg_id"
select="mybatis.mapper.MessageDetailMapper.getMessageByMsgId" />
resultMap>
其中标签中的property是Message实体中的一个属性,他对应的类型是一个实体(另一张表),column属性中对应的是message这张表中的msg_id字段,他通过这个表中的msg_id字段与MessageDetail表关联起来:
<select id="getMessageByMsgId" parameterType="string" resultType="vo.MessageDetail">
select
<include refid="allColumns"/>
from
tb_message_detail
where
msg_id = #{msgId}
select>
#{msgId}这里面的这个值应该是要和getMessageByMsgId()中对应
<resultMap id="userReuslt" type="vo.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<collection property="userContacts" column="id"
select="mybatis.mapper.UserContactMapper.getUserContactByUserId" />
resultMap>
这里面额使用原理与一对一的映射是相似的区别之处在于使用了标签
Mybatis中的缓存有一级缓存、二级缓存、自定义缓存
一级缓存
一级缓存默认是开启的,他作用与的是同一个sqlSession
二级缓存
二级缓存的范围是多个sqlSession,在映射文件中加入标签即可,使用耳机缓存的时候结果的实体需要实现Serializable接口
public class Boys implements Serializable {
<mapper namespace="com.example.mybatis.mybatis.BoysMapper">
<cache />
<sql id="allCoulm">
id,boyName,userCP
sql>
<select id="selectBoys" resultType="Boys">
select
<include refid="allCoulm">include>
from
boys
where
id = #{id}
select>
mapper>
自定义缓存
自定义缓存我们需要实现Cash接口,例如在这里我们可以实现自己连接Redis来实现我们的自定义缓存,我们也可以借用结合来实现
public class SelfCash implements Cache {
在使用缓存的时候需要我们在标签中指明:
<cache type="com.example.mybatis.mybatis.SelfCash"/>
if、t e s t的使用
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state = 'ACTIVE '
<if test="title != null">
AND title like #{title}
if>
select>
choos e、when、otherwise
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
when>
<otherwise>
AND featured = 1
otherwise>
choose>
select>
t r im、whe r e、s e t的使用
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},if>
<if test="password != null">password=#{password},if>
<if test="email != null">email=#{email},if>
<if test="bio != null">bio=#{bio}if>
set>
<where> id=#{id} where>
update>
<trim prefix="SET" suffixOverrides=",">
...
trim>
for each的使用
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
foreach>
select>
bind的使用
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
select>