eg:Mybatis
。Mybatis
之后,dao
层会更简单。你只需要写一个dao
接口,然后写一个SQL
语句,dao
层就已经写完了。API
,少量编码ORM
Object Relational Mapping
对象关系映射的思想
对象 - Java中的对象 关系-关系型数据库中表的记录 映射 - 一对一关联起来
java
项目中的每一个实体类,对应数据库中的一个表;
java
类中的属性,对应数据库表中的字段。
java
类中的一个实体对象,对应一个数据表中的一条记录
全自动ORM
框架:hibernate
。
通过操作实体对象,就可以完成对数据库表中记录的操作。复杂业务性能低下,不可定制,学习成本高。
半自动的ORM
框架:Mybatis
,ibatis
基于ORM
,但是SQL
语句需要我们自己编写。自己优化SQL
,可定制性强,效率高。
Mybatis
&原生JDBC
原生jdbc
SQL
参数并执行SQL
语句相同的内容已经抽取到工具类中(上述删除线标识的步骤)
模板化(步骤不变,部分步骤相同,上述加粗的内容)的操作,也是可以抽取封装的。
但是相同的步骤中又有不同的地方,不同的地方如果也要实现解耦,需要通过配置来实现
Mybatis
解决方案
SQL
语句和设置到SQL
中的参数抽取到xml
配置文件中Mybatis
框架抽取后的样子
Mybatis
简介
DAO
)框架,封装共性内容让使用者只关注SQL
本身。结合了ORM
思想,是一个ORM
半自动的框架。Mybatis
之后,我们只需要定义一个Dao
层接口+存储SQL
的配置文件(Mybatis
映射配置文件),就可以完成Dao
层内容。Mybatis
快速入门Mybatis
快速入门步骤导入Jar包
mybatis-3.5.3.jar
mysql-connector-java-5.1.37-bin.jar
junit-4.12.jar
编写Mybatis
核心配置文件:mybatis-config.xml
标签中映射文件路径不是.
是/
建议复制,不要手敲编写POJO
类和Dao
层接口,初始化数据库表及数据
编写映射文件:StudentDao.xml
,在映射文件中编写SQL语句
测试
小经验
直接生成mybatis
的两类xml
文件
Mybatis
映射配置文件和接口之间相互跳转
核心配置文件
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///web17_mybatis01"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="dao/StudentDao.xml"/>
mappers>
configuration>
映射配置文件
<mapper namespace=".dao.StudentDao">
<select id="findById" resultType="domain.Student">
select * from student where id = 1;
select>
mapper>
接口StudentDao.java
public interface StudentDao {
Student findById();
}
测试类MybatisTest.ava
public class MybatisTest {
@Test
public void test01() throws IOException {
// 配置文件位置
String resource = "mybatis-config.xml";
// 加载配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过构建的方式创建了SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取session对象
// SqlSession session = sqlSessionFactory.openSession();
// JDK7 新特性 try-with-resources
/**
* try(创建各种连接对象){
* 使用连接
* }
* 不需要自己关闭连接,这个结构会在执行完代码之后自动帮你关闭
*/
// 获取session对象
try (SqlSession session = sqlSessionFactory.openSession()) {
// 通过连接对象,获取dao层实现类对象
StudentDao studentDao = session.getMapper(StudentDao.class);
// 调用dao层实现类对象中的方法
Student student = studentDao.findById();
System.out.println("student = " + student);
// 提示让删除,因为已经关闭过了
// session.close();
}
}
}
Resources
读取配置的文件的工具类,底层使用的是classLoad对象.getResourceAsStream
InputStream getResourceAsStream
(“配置文件相对于类路径的相对路径”);
SqlSessionFactoryBuilder
获取SqlSessionFactory
工厂对象的功能类。
// 通过构建的方式构建了一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//xxxBuilder.build()
SqlSessionFactory
org.apache.ibatis.session.SqlSessionFactory
:获取 SqlSession
对象的工厂接口。
SqlSession openSession()
获取SqlSession
对象,并开启事务,需要手动提交事务
SqlSession openSession(boolean autoCommit)
获取SqlSession
对象,true
表示自动提交
org.apache.ibatis.session.SqlSession
:sql会话对象接口。用于执行SQL
、管理事务、接口代理。
void | commit() | 提交事务 |
void | rollback() | 回滚事务 |
T | getMapper(Class cls) | 获取指定接口的代理实现类对象 |
void | close() | 释放资源 |
<mapper namespace="dao.StudentDao">
<select id="findById" resultType="domain.Student">
select * from student where id = 2
select>
mapper>
编码完成如下需求:
// 需求1:查询id为1的学生(强制要求)
// 需求2:创建一个Student对象并保存到数据库(参数和成员变量名保持一致)(成功了吗?)
// 需求3:根据学生姓名和年龄查询用户,传两个参数(多个参数怎么办?)
// 需求4:完成需求3,传一个参数。
映射配置文件StudentDao.xml
,保证这个文件的名字和接口名完全一致,且在同一个包下。
<mapper namespace="dao.StudentDao">
<insert id="save">
insert into student values(#{id},#{firstName},#{age})
insert>
<select id="findByNameAndAge" resultType="domain.Student">
select * from student where name = #{namex} and age = #{agex}
select>
<select id="findByCondition"
resultType="domain.Student">
select * from student where name = #{name} and age = #{age}
select>
<select id="findByConditionMap" resultType="domain.Student">
select * from student where name = #{namex} and age = #{agex}
select>
mapper>
核心配置文件
<mappers>
<mapper resource="dao/StudentDao.xml"/>
mappers>
Dao
层接口StudentDao
public interface StudentDao {
@Select("select * from student where id = 1")
Student findById();
// 需求1:查询id为1的学生(强制要求)
Student findById2(@Param("id") Integer id);
// 需求2:创建一个Student对象并保存到数据库
void save(Student stu);
// 需求3:根据学生姓名和年龄查询用户,传两个参数(多个参数怎么办?)
List<Student> findByNameAndAge(@Param("name") String name, @Param("agex") Integer age);
// 需求4:完成需求3,传一个参数。
// 4.1 把两个参数封装成一个实体对象
// 如果把多个参数封装到一个实体对象中,参数类型有局限性,不能是两个一样的条件做范围查询
List<Student> findByStudent(Student stu);
// 4.2 把两个参数封装到Map集合中
List<Student> findByMap(Map<String,Object> map);
}
测试类
/**
* @Author Vsunks.v
* @Date 2020/8/7 11:19
* @Description:
*/
public class MybatisDemo {
public static void main(String[] args) throws Exception {
// 指定核心配置文件
String resource = "mybatis-config.xml";
// 把核心配置文件加载成流 Resources为我们提供的方便读取配置文件的工具类
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过构建的方式构建了一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// SqlSession 就相当于我们之前的 Connection
// JDK7新特性 try-with-resource
// 需要释放资源的动作,自动释放在try()中开启的一些流、会话......
try (SqlSession session = sqlSessionFactory.openSession(true)) {
// getMapper getDao 获取一个指定接口的实现类对象
// 底层是动态代理
// 动态代理可以增强有接口的类、接口(增强接口,就是现实接口)
// 就相当于我们自己new StudentDaoImpl();
// 这个StudentDaoImpl是Mybatis通过动态代理帮我们自动生成并且赋值给了studentDao
StudentDao studentDao = session.getMapper(StudentDao.class);
/*List students = studentDao.findAll();
System.out.println("students = " + students);*/
// 测试根据id查询
/*Student student = studentDao.findById(1);
System.out.println("student = " + student);*/
// 测试添加学生
//studentDao.saveStudent(new Student(null,"凤姐",20));
// 根据名称和年龄查询用户,参数是两个基本类型
//List students = studentDao.findByNameAndAge("美女", 20);
//System.out.println("students = " + students);
// 根据名称和年龄查询用户,参数是一个student对象
//List students = studentDao.findByUser(new Student(null, "凤姐", 20));
//System.out.println("students = " + students);
// 根据名称和年龄查询用户,参数是一个map集合
HashMap<String, Object> map = new HashMap<>();
map.put("name", "王二蛋");
map.put("age", 20);
System.out.println("studentDao.findByMap(map) = " + studentDao.findByMap(map));
// 手动提交
//session.commit();
// session.close();
}
}
}
核心配置文件中各种配置的顺序,需要符合官方要求,否则(运行时)会报错。错误提示如下:
引入外部的properties文件,一般用作引入数据库链接参数的配置。
核心配置文件
<configuration>
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<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="dao/StudentDao.xml"/>
mappers>
configuration>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.115.130:3306/db1
# 数据库用户名可以是username之外任意名称
jdbc.username=root
jdbc.password=root
Settings
(重要)
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="LOG4J"/>
settings>
TypeAliases
(了解)
<typeAliases>
<package name="domain"/>
typeAliases>
起别名后,在映射配置文件中类型处可以直接使用别名(不区分大小写)
<select id="findByMap" resultType="Student">
select * from student where first_username=#{name}
and
age=#{age}
select>
系统为常见的类型起好了别名,详见官方文档
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
整体使用频率不高,因为插件会自动生成,而且写全的更好理解。
Environments
(了解)会配置连接四要素即可
<environments default="dev">
<environment id="dev">
<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>
<environment id="test">
<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
(重点)
<mappers>
<package name="dao"/>
mappers>
Mybatis
配置文件本质是新建了一个文件模板,按照下面的方式新建两个模板即可(Mybatis-config
、Mybatis-Mapper
)
接口
和映射配置文件
跳转使用一个插件free-idea-mybatis
。
这个插件是个zip包,不要解压,直接安装即可。
已知bug:
多个模块之间有相同内容会提示/跳转错误,卸载其他模块即可。
#{} MyBatis 会创建 PreparedStatement
参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法。
${} 会做字符串的拼接,把${参数名}中参数名对应的值拼接进SQL语句,可能会有SQL注入的风险,效率更低。但是,如果你需要在SQL语句中直接插入一个不转义的字符串,可以使用这种方式。一般情况下会把表名或者字段名通过这方方式传递到SQL语句中,比方说 ORDER BY后面的列名。
/**
* mybatis工具类,可以实现的功能
* 1. 自动维护SQLSessionFactory
* 2. 获取SQLSession
* 3. 获取dao接口代理对象
*
* @Author Vsunks.v
* @Date 2020/12/4 16:30
* @Blog blog.sunxiaowei.net
* @Description:
*/
public class MybatisUtils {
// 私有构造
private MybatisUtils() {
}
// 维护一个SQLSessionFactory对象
private static SqlSessionFactory ssf;
// 静态代码块初始化SQLSessionFactory
static {
// 加载配置文件
String resource = "mybatis-config.xml";
try {
// 获取流
InputStream is = Resources.getResourceAsStream(resource);
// 通过builder build一个SqlSessionFactory
ssf = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
//2. 获取SQLSession
public static SqlSession getSqlSession() {
return ssf.openSession(true);
}
// 3. 获取dao接口代理对象
public static <T> T getMapper(Class<T> tClass){
return getSqlSession().getMapper(tClass);
}
}
tisUtils {
// 私有构造
private MybatisUtils() {
}
// 维护一个SQLSessionFactory对象
private static SqlSessionFactory ssf;
// 静态代码块初始化SQLSessionFactory
static {
// 加载配置文件
String resource = "mybatis-config.xml";
try {
// 获取流
InputStream is = Resources.getResourceAsStream(resource);
// 通过builder build一个SqlSessionFactory
ssf = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
//2. 获取SQLSession
public static SqlSession getSqlSession() {
return ssf.openSession(true);
}
// 3. 获取dao接口代理对象
public static T getMapper(Class tClass){
return getSqlSession().getMapper(tClass);
}
}