JDBC编程和ORM模型
JDBC回顾
A-加载驱动
导入JDBC连接数据库的jar包,利用CLASS.forName加载驱动;
B-获取连接
利用DriverManager获取Connection,然后创建Statement;
C-执行SQL语句
利用Statement执行Sql语句并结合ResultSet处理结果i集映射java对象;
D-关闭资源
先开后关的顺序关闭ResultSet,Statement,Connection等资源;
JDBC的弊端
a-硬编码
SQL语句在java代码中,不能很好的分离数据库语句和java语句明早成代码不易维护;
b-sql参数固定
Sql语句的参数固定,使用sql语句不灵活,无法满足多变的场景;
c-代码重复度高
大量的重复代码。以resultSet为例,每次哦都需要重复解析结果集。
d-底层技术
jdbc属于底层的技术,不支持分布式,对于复杂场景应对不好;
1.hibernate是全自动化ORM;Mybatis是半自动化ORM。
2.Hibernate是一个重量级的框架,内部生成SQL语句,反射操作太多,导致性能下降;
Mybatis是一个轻量级的框架,需要自己写sql语句,有较少的反射操作。
3.Hibernate简化dao层,不用考虑SQL语句的编写和结果映射,重点放在业务逻辑上;Mybatis需要手写SQL语句以及结果映射。
4.Hibernate不方便做SQL优化,遇到较复杂的SQL语句需要绕过框架实现复杂,对多字段的结构进行部分映射困难。
Mybatis不仅可以做SQL优化还可以SQL与Java分离,还可以自行编写映射关系,复杂的SQL语句Mybatis效率更高
Mybatis优势:
可以进行更细致的sql优化,容易掌握;
Hibernate优势:
dao层开发比mybatis简单,mybatis需要维护SQL和结果映射,hibernate数据库移植性好;
mybatis解决了jdbc的问题:
1.数据库连接创建,释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决
可解决此问题。
解决:在SqlMapConfig.xml配置数据连接池,使用连接池管理数据库链接。
2.sql语句写在代码中,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要
改变java代码。
解决:将sql语句配置在xxxMapper.xml文件中与java代码分离。
3.向sql语句传参数麻烦,因为where条件不一定,可能多可能少,占位符需要和参数11对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType
定义输入参数的类型。
4.对比 结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
为什么选择Mybatis?
简单易学,灵活度高,功能完整,提供了连接管理,缓存支持,线程支持,事务管理等功能,还提供了其他ORM模型发支持。
什么是MyBatis?
MyBatis是一款ORM模型,支持定制化sql,存储过程以及高级映射。可以使用简单的xml或注解来配置和映射原生信息,将接口和java的pojo(plain old java Objects 普通的java对象)映射成数据库中的记录
MyBatis生命周期
1)读取 MyBatis 配置文件:mybatis - config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
2)加载映射文件:映射文件为 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句。需要在 MyBatis 配置文件 mybatis - config.xml 中加载,mybatis - config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等。
7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型,输入参数映射过程类似于 JDBC 对 preparedStatement 设置参数的过程。
8)输出结果映射:输出结果类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型,输出结果映射过程类似于 JDBC 对结果集的解析过程。
什么是ORM?
对象关系映射,用于实现面向对象编程语言的不同类型系统的数据之间的转换;
Mybatis的优势: 简单易学,灵活度高,功能完整。
Mybatis的生命周期:SqlSession FactoryBuilder ,SqlSessionFactory,SqlSession,SQL Mapper;
MyBatis配置文件
数据库连接部分
<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>
<!-- 和spring整合后 environments配置将废除 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <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>配置数据库环境信息,注册数据原,配置数据库事务,默认使用default
注意:<transactionManager>配置数据库事务;
<dataSource>数据源
- unpooled-非连接池数据源
- pooled-连接池的数据源
- jndi-JNDI数据源
- 自定义数据源-其他类型数据源
<properties resource="jdbc.properties"> </properties> <typeAliases> <!-- <typeAlias alias="aaa" type="pojo.Student"/>--> <package name="pojo"/> </typeAliases>
Mybatis配置文件的功能
构建SqlSessionFactory的依据,对整个MyBatis体系深远;
properties元素的功能
配置属性的元素,可以在配置文件的上下文中使用该属性;
enviroments元素的功能
配置环境信息,注册数据源,配置数据库事务;
mappers元素的功能
用来在mybatis初始化的时候引入映射器
MyBatis动态代理和映射器
什么是mapper的动态代理?
在接口中有方法的返回值定义,参数的定义,方法名,在sqlMapper.xml中也对这接口赋予了赋值,这时候dao的实现就显得多余,这是mybatis可以帮助我们自动产生实现类,并可以调取方法得到结果,这就是Mybatis的mapper动态代理。
动态代理规范
Mapper接口开发方法只需要程序员编写Mapper接口(相当于dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象模型,代理对象的方法体同上边Dao接口实现类的方法。
Mapper接口开发需要遵循一下规范:
- Mapper.xml文件中的namespace与mapper接口的类路径相同。
- Mapper接口方法名和Mapper.xml中定义的每个statement中的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
使用sqlsession的方法getmapper()让Mybatis自动生成对应接口的实现对象。
selectOne和selectList
动态代理对象,调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
select元素
- id 唯一标识,接口中的方法名;
- parameterType参数类型;
- resultType结果的类型;
- resultMap复杂的结果集映射关系
语法规则:<select 属性 = "值">查询类sql语句</select>
例如:
<resultMap id="querry_book" type="book">
<id property="bookId" column="book_id"/>
<result property="bookName" column="book_name"/>
<result property="bookPrice" column="book_price"/>
<result property="bookAuthor" column="book_author"/>
<result property="bookNum" column="book_num"/>
</resultMap>
<select id="queryBookByName" parameterType="string" resultMap="querry_book">
select *
from book
<where>
<if test="bookName != null">
and book_name like concat('%',#{bookName},'%')
</if>
<if test="bookAuthor != null">
and book_author like concat('%',#{bookAuthor},'%')
</if>
</where>
</select>
public List<Book> queryBookByName(Book book);
insert元素
- id 唯一标识,接口中的方法名;
- parameter Type 参数的类型;
- keyProperty 表示以那个列作为属性的主键,不能和keyColumn同时使用
- keyColumn 指明那一列是主键,不能和keyProperty同时使用
- useGeneratedKeys 使用JDBC的getGeneratedKeys方式来取有数据库内部生成的主键
语法规则:<insert 属性=“值”> 新增类SQL语句</insert>(当主键在数据库中为自增字段时候,新增成功后回填字段)
public Integer addBook(Book book);
<insert id="addBook" parameterType="book">
insert into book(book_name, book_price, book_author, book_num)
values (#{bookName}, #{bookPrice}, #{bookAuthor}, #{bookNum})
</insert>
update元素和delete元素
语法规则:<update 属性=“值”> 查询类的sql语句</update>
<delete 属性=“值”>查询类sql语句</delete>
<update id="updateBook" parameterType="book">
update book
set book_num = #{bookNum}
where book_id = #{bookId}
</update>
public Integer updateBook(Book book);
为什么要用resultMap元素
- 定义映射规则
- 级联操作-多表之间存在主外键关系时,主表和从表之间的关联关系
- 类型转换-数据库字段的类型和pojo类属性的类型转换
使用pojo封装结果集
- property-映射到列结果的字段或者属性,通常指pojo的属性;
- column-对应的数据库字段;
- javaType-Java类型,可以时基本数据类型,也可以是类完全限定名;
- jdbcType-jdbc的类型,基本支持所有常用的数据库类型;
<!-- 一对多-->
<resultMap id="CardResultMap" type="pojo.Card">
<id property="cardId" column="card_id"/>
<result property="cardName" column="card_name"/>
<result property="cardMoney" column="card_money"/>
<collection property="bookList" ofType="pojo.Book">
<id property="bookId" column="book_id"/>
<result property="bookName" column="book_name"/>
<result property="bookPrice" column="book_price"/>
</collection>
</resultMap>
一、动态 SQL 概述
定义:根据不同条件动态拼接 SQL 语句,实现对数据库更精准的操作。
实现方式:通过映射器配置文件或注解实现。
常用动态 SQL 元素:
- if:单条件分支判断
- choose (when, otherwise):多条件分支判断(类似 switch)
- trim (where, set):处理 SQL 拼接问题的辅助元素
- foreach:循环语句,常用于 in 语句等列举条件
- bind:自定义上下文变量,传递参数
二、if 元素
功能:进行单条件分支判断
语法:
<if test="条件"></if>
注意事项:
- 拼接 SQL 语句时需注意 AND 和逗号的使用
- 条件判断基于传入的参数值
三、choose、when、otherwise 元素
适用场景:
- 当新闻编号不为空,仅用新闻编号作为查询条件
- 当新闻编号为空而标题不为空,用标题进行模糊查询
- 当新闻编号和标题都为空,要求作者不为空
功能:多条件分支判断,类似 Java 的 switch 语句
语法:
<choose>
<when test="条件"></when>
<otherwise></otherwise>
</choose>
注意事项:
- 拼接 SQL 语句时注意 AND 和逗号的使用
- 若没有条件匹配,将执行 otherwise 中的内容
四、trim、where、set 元素
where 元素
功能:智能处理 WHERE 子句的拼接
语法:
<where>
<if test="title != null">and title = #{title}</if>
</where>
说明:
- 仅当至少有一个子元素条件成立时,才插入 "WHERE" 子句
- 自动去除语句开头的 "AND" 或 "OR"
set 元素
功能:智能处理 UPDATE 语句中的 SET 子句
<set>
<if test="条件"></if>
</set>
说明:
- 在包含的语句前输出 "SET"
- 自动忽略语句结尾的逗号
- 若包含内容为空会出错
trim 元素
功能:更灵活地处理 SQL 拼接,可替代 where 和 set
语法:
属性说明:
- prefix:需要拼接的前缀(如 where、set)
- suffixOverrides:忽略指定的后缀文本(用管道分隔)
- prefixOverrides:忽略指定的前缀文本(用管道分隔)
- suffix:需要拼接的后缀
示例:
<trim prefix="where" suffixOverrides="and" prefixOverrides="or"></trim>
五、foreach 元素
功能:循环处理集合或数组,常用于 in 语句
语法:
<foreach item="" index="" collection="" open="" separator="" close="">
#{item}
</foreach>
属性说明:
- item:循环中的当前元素
- index:当前循环元素的位置下标
- collection:传入的参数(数组或集合)
- open:集合元素包装的起始符号
- close:集合元素包装的结束符号
- separator:元素间的分隔符
适用场景:
- 处理 IN 条件中的多个值
- 批量插入、更新操作
六、bind 元素
功能:自定义上下文变量,方便参数处理
语法:
<bind name="" value="_parameter"></bind>
属性说明:
- name:自定义变量名
- value:变量值,_parameter 表示传递进来的参数
示例:
<bind name="name1" value="'%' + _parameter + '%'"></bind>
适用场景:
- 处理模糊查询的通配符拼接
- 统一参数格式转换
小结:
- if:单条件分支判断
- choose (when、otherwise):多条件分支判断
- trim (where、set):处理 SQL 拼接问题的辅助元素
- foreach:循环处理集合或数组,适用于 in 语句等
- bind:自定义上下文变量,方便参数处理
Mybatis映射器注解
一、映射器注解概述
1.1 映射器配置文件的缺陷
- 繁琐:配置文件书写复杂,需掌握大量 XML 语法规则
- 不直观:配置文件与接口仅通过名称关联,对应关系不清晰
1.2 常用注解分类
注解类型 | 功能说明 |
---|---|
基本注解 | 实现简单的增删改查(CRUD)操作 |
结果映射注解 | 定义数据库字段与 JavaBean 属性的映射关系,支持级联映射 |
动态 SQL 注解 | 实现注解方式的动态 SQL 拼接 |
二、基本注解(CRUD 操作)
2.1 @Insert(新增操作)
功能:替代 XML 配置中的<insert>
元素,实现数据新增
语法:
@Insert("SQL语句")
public 返回值 方法名(参数);
@Insert("insert into news (title, author) values (#{title}, #{author})")
public void addNews(News news);
主键处
@Options(useGeneratedKeys = true, keyProperty = "newsId") @Insert("insert into news (title, author) values (#{title}, #{author})") public void addNews(News news);
@SelectKey( statement = "select max(newsid) + 1 as newsId from news", keyProperty = "newsId", resultType = int.class, before = true ) @Insert("insert into news (newsId, title, author) values (#{newsId}, #{title}, #{author})") public void addNews(News news);
2.2 @Delete(删除操作)
功能:替代 XML 配置中的<delete>
元素,实现数据删除
语法
@Delete("SQL语句")
public 返回值 方法名(参数);
@Delete("delete from news where newsid = #{newsId}")
public void delNews(News news);
2.3 @Update(更新操作)
功能:替代 XML 配置中的<update>
元素,实现数据更新
语法:
@Update("SQL语句")
public 返回值 方法名(参数);
@Update("update news set title = #{title}, author = #{author} where newsid = #{newsId}")
public void updateNews(News news);
2.4 @Select(查询操作)
功能:替代 XML 配置中的<select>
元素,实现数据查询
语法:
@Select("SQL语句")
public 返回值 方法名(参数);
@Select("select * from news where title = #{title}")
public List<News> getNewsByTitle(News news);
2.5 多参数传递方式
- Map 方式:通过 Map 集合传递多个参数,SQL 中通过键名取值
- JavaBean 方式:通过实体类对象传递参数,SQL 中通过属性名取值
- @Param 方式:通过注解指定参数名,适用于参数较少的场景
@Select("select * from news where title = #{title} and author = #{author}") public List<News> getNews(@Param("title") String title, @Param("author") String author);
三、结果映射注解
3.1 @Results(定义结果映射)
功能:建立数据库字段与 JavaBean 属性的映射关系,支持主键标识
语法:
@Results({
@Result(id = 是否为主键, column = "数据库字段名", property = "JavaBean属性名"),
// 多个字段映射...
})
public 返回值 方法名(参数);
@Select("select newsid as newsId, title, author from news where newsid = #{newsId}")
@Results({
@Result(id = true, column = "newsId", property = "newsId"),
@Result(column = "title", property = "title"),
@Result(column = "author", property = "author")
})
public News getNewsById(Integer newsId);
3.2 @ResultMap(复用结果映射)
功能:复用已定义的结果映射(支持 XML 中的<resultMap>
或注解中的@Results
)
使用场景:
- 复用 XML 中的结果映射:
// XML中定义:<resultMap id="newsResultMap">...</resultMap> @Select("select * from news where newsid = #{newsId}") @ResultMap("newsResultMap") // 引用XML中的resultMap public News getNewsById(Integer newsId);
- 复用注解中的结果映射:
// 先定义带id的@Results @Results(id = "newsResult", value = { @Result(id = true, column = "newsId", property = "newsId"), @Result(column = "title", property = "title") }) @Select("select * from news where newsid = #{newsId}") public News getNewsById(Integer newsId); // 复用已定义的@Results @Select("select * from news where author = #{author}") @ResultMap("newsResult") // 引用注解中的结果映射id public List<News> getNewsByAuthor(String author);
注意:一个 SQL 语句不能同时使用@Results
和@ResultMap
3.3 级联映射注解
3.3.1 @One(一对一映射)
功能:实现一对一关联查询,如 “用户 - 身份证” 关联
语法:
@Result(
column = "关联字段名",
property = "关联属性名",
one = @One(
select = "关联查询的Mapper方法全路径",
fetchType = 加载方式
)
)
@Select("select * from user where userid = #{userId}")
@Results({
@Result(id = true, column = "userid", property = "userId"),
@Result(column = "username", property = "username"),
// 一对一关联查询身份证信息
@Result(
column = "card_id", // 用户表中关联身份证表的字段
property = "idCard", // User类中关联的IdCard属性
one = @One(
select = "com.example.dao.IdCardMapper.getCardById",
fetchType = FetchType.EAGER // 即时加载(EAGER)/延迟加载(LAZY)
)
)
})
public User getUserWithIdCard(Integer userId);
3.3.2 @Many(一对多映射)
功能:实现一对多关联查询,如 “分类 - 文章” 关联
语法:
@Result(
column = "关联字段名",
property = "关联属性名",
many = @Many(
select = "关联查询的Mapper方法全路径",
fetchType = 加载方式
)
)
@Select("select * from category where categoryid = #{categoryId}")
@Results({
@Result(id = true, column = "categoryid", property = "categoryId"),
@Result(column = "categoryname", property = "categoryName"),
// 一对多关联查询文章列表
@Result(
column = "categoryid", // 分类表中关联文章表的字段
property = "newsList", // Category类中关联的List<News>属性
many = @Many(
select = "com.example.dao.NewsMapper.getNewsByCategoryId",
fetchType = FetchType.LAZY // 延迟加载
)
)
})
public Category getCategoryWithNews(Integer categoryId);
四、动态 SQL 注解
4.1 脚本 SQL(<script>
标签)
功能:直接在注解中嵌入 XML 风格的动态 SQL,适用于简单场景
语法:在 SQL 语句外层包裹<script>
标签,内部使用if
、choose
等动态元素
示例:
@Select("<script>" +
"select * from news " +
"<where>" +
" <if test='title != null'>and title like #{title}</if>" +
" <if test='author != null'>and author = #{author}</if>" +
"</where>" +
"</script>")
public List<News> getNewsByCondition(News news);
4.2 Provider 注解(动态构建 SQL)
功能:通过外部类动态生成 SQL 语句,适用于复杂动态 SQL 场景
常用注解:
注解 | 对应操作 | 说明 |
---|---|---|
@SelectProvider | 查询 | 动态生成 SELECT 语句 |
@InsertProvider | 新增 | 动态生成 INSERT 语句 |
@UpdateProvider | 更新 | 动态生成 UPDATE 语句 |
@DeleteProvider | 删除 | 动态生成 DELETE 语句 |
使用步骤
创建 SQL 提供类:定义生成 SQL 的方法(返回 String 类型)
public class NewsSqlProvider { // 动态生成查询SQL public String getNewsByCondition(News news) { StringBuilder sql = new StringBuilder("select * from news where 1=1"); if (news.getTitle() != null) { sql.append(" and title like concat('%', #{title}, '%')"); } if (news.getAuthor() != null) { sql.append(" and author = #{author}"); } return sql.toString(); } // 动态生成更新SQL public String updateNews(News news) { StringBuilder sql = new StringBuilder("update news set "); if (news.getTitle() != null) { sql.append("title = #{title}, "); } if (news.getAuthor() != null) { sql.append("author = #{author}, "); } // 去除末尾多余的逗号 sql.deleteCharAt(sql.lastIndexOf(",")); sql.append(" where newsid = #{newsId}"); return sql.toString(); } }
在 Mapper 接口中关联 Provider:
public interface NewsMapper { // 关联查询SQL提供类 @SelectProvider(type = NewsSqlProvider.class, method = "getNewsByCondition") List<News> getNewsByCondition(News news); // 关联更新SQL提供类 @UpdateProvider(type = NewsSqlProvider.class, method = "updateNews") void updateNews(News news); }
4.3 SQL 语句构造器
功能:通过 MyBatis 提供的SQL
类构建 SQL 语句,避免手动拼接字符串(减少语法错误)
核心方法:
方法 | 说明 |
---|---|
SELECT(String... columns) |
指定查询字段 |
FROM(String table) |
指定查询表 |
WHERE(String condition) |
添加 WHERE 条件 |
AND(String condition) / OR(String condition) |
追加 AND/OR 条件 |
INSERT_INTO(String table) |
指定插入表 |
VALUES(String columns, String values) |
指定插入的字段和值 |
UPDATE(String table) |
指定更新表 |
SET(String... sets) |
指定更新的字段和值 |
DELETE_FROM(String table) |
指定删除表 |
public class NewsSqlProvider {
public String getNewsByCondition(News news) {
return new SQL() {{
SELECT("*");
FROM("news");
if (news.getTitle() != null) {
WHERE("title like concat('%', #{title}, '%')");
}
if (news.getAuthor() != null) {
AND("author = #{author}"); // 追加AND条件
}
}}.toString();
}
public String updateNews(News news) {
return new SQL() {{
UPDATE("news");
if (news.getTitle() != null) {
SET("title = #{title}");
}
if (news.getAuthor() != null) {
SET("author = #{author}");
}
WHERE("newsid = #{newsId}");
}}.toString();
}
}
五、总结
基本注解:
@Insert
/@Delete
/@Update
/@Select
:分别实现增删改查操作,支持主键回填和多参数传递
结果映射注解:
@Results
:定义数据库字段与 JavaBean 属性的映射关系,支持主键标识@ResultMap
:复用已定义的结果映射(XML 或注解方式)@One
/@Many
:实现一对一、一对多级联映射,支持即时 / 延迟加载
动态 SQL 注解:
- 脚本 SQL:通过
<script>
标签嵌入 XML 风格动态 SQL - Provider 注解:
@SelectProvider
/@InsertProvider
等,通过外部类动态生成 SQL - SQL 语句构造器:使用
SQL
类构建 SQL,避免手动拼接错误
- 脚本 SQL:通过
核心优势:注解方式简化配置,减少 XML 文件依赖,使 SQL 与接口更紧密关联,提高代码可读性和维护性