传统JDBC连接数据库
我们在学习心得(一)中说到,传统JDBC的弊端,下面我们通过代码来看一下:
public class DbUtil {
private static String driver;
private static String url;
private static String username;
private static String password;
static {
driver = "com.mysql.jdbc.Driver";
url = "jdbc:mysql://localhost:3306/mybatis";
username = "root";
password = "123456789";
}
public static Connection open() {
try {
Class.forName(driver);
return (Connection) DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void close(Connection conn) {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
这是一个连接数据库的工具类,定义了数据库连接属性,声明打开连接和关闭连接两个方法。
数据操作
非对象操作
public class Jdbc01 {
public static void main(String[] args) {
insert("chaos",18);
}
static void insert(String name, int age) {
String sql = "INSERT INTO user(user_name, age) VALUES (?, ?)";
Connection conn = DbUtil.open();
try {
PreparedStatement psm = (PreparedStatement) conn.prepareStatement(sql);
psm.setString(1, name);
psm.setInt(2, age);
psm.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtil.close(conn);
}
}
}
面向对象操作
public class Jdbc02 {
public static void main(String[] args) {
User user = new User();
user.setUsername("chaos");
user.setAge(18);
insert(user);
//查询
user = query(1);
}
static void insert(User user) {
String sql = "insert into user(user_name,age) value(?,?)";
Connection conn = DbUtil.open();
try {
PreparedStatement psm = (PreparedStatement) conn.prepareStatement(sql);
psm.setString(1, user.getUsername());
psm.setInt(2, user.getAge());
psm.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtil.close(conn);
}
}
static User query(int id) {
String sql = "select * from user where id=?";
Connection conn = DbUtil.open();
try {
PreparedStatement psm = (PreparedStatement) conn.prepareStatement(sql);
psm.setInt(1, id);
ResultSet rs = psm.executeQuery();
if (rs.next()) {
String name = rs.getString(2);
User user = new User();
user.setId(id);
user.setUsername(name);
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtil.close(conn);
}
return null;
}
}
通过实际操作,我们可以看到在学习心得(一)中总结的传统JDBC弊端。
下面我们来看一下Mybatis是怎么做的。
什么是Mybatis?
官网给出的解释:
What is MyBatis?
MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.
什么是Mybatis?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(普通的 Java对象)映射成数据库中的记录。
什么是ORM框架?
ORM(Object Relational Mapping):对象关系映射,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
Mybatis工作原理浅析
Mybatis工作流程图如下:
- 加载Mybatis配置文件–mybatis-config.xml ,mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等属性,例如数据库连接属性。
// 加载配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource);
- 加载映射文件,映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
<mappers> <mapper resource="mapper/UserMapper.xml"/> <mapper resource="mapper/XXXMapper.xml"/> ... </mappers>
- 构建会话工厂–SqlSessionFactory,通过 MyBatis 的环境等配置信息(即全局配置文件mybatis-config.xml中的配置信息)构建会话工厂 SqlSessionFactory。
// 构建会话工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- 获取会话对象–SqlSession,由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
// 获取会话对象 SqlSession session = sqlSessionFactory.openSession();
public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); <E> List<E> selectList(String statement); <E> List<E> selectList(String statement, Object parameter); <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds); <K, V> Map<K, V> selectMap(String statement, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); <T> Cursor<T> selectCursor(String statement); <T> Cursor<T> selectCursor(String statement, Object parameter); <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds); void select(String statement, Object parameter, ResultHandler handler); void select(String statement, ResultHandler handler); void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); int insert(String statement); int insert(String statement, Object parameter); int update(String statement); int update(String statement, Object parameter); int delete(String statement); int delete(String statement, Object parameter); void commit(); void commit(boolean force); void rollback(); void rollback(boolean force); List<BatchResult> flushStatements(); @Override void close(); void clearCache(); Configuration getConfiguration(); <T> T getMapper(Class<T> type); Connection getConnection(); }
- 执行器–Executor,MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
// 调用执行查询 User user = session.selectOne("com.chaos.demo.mybatis.pojo.UserMapper.selectUser", 1); System.out.println(user);
public interface Executor { .... <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; .... }
// public abstract class BaseExecutor implements Executor{ .... @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } .... }
- 映射声明–MappedStatement,在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
public final class MappedStatement { private String resource; private Configuration configuration; private String id; private Integer fetchSize; private Integer timeout; private StatementType statementType; private ResultSetType resultSetType; private SqlSource sqlSource; private Cache cache; private ParameterMap parameterMap; private List<ResultMap> resultMaps; private boolean flushCacheRequired; private boolean useCache; private boolean resultOrdered; private SqlCommandType sqlCommandType; private KeyGenerator keyGenerator; private String[] keyProperties; private String[] keyColumns; private boolean hasNestedResultMaps; private String databaseId; private Log statementLog; private LanguageDriver lang; private String[] resultSets; ... }
- 输入参数映射,输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
- 输出结果映射,输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
总结:今天的学习我们复习了一下JDBC操作数据库的流程以及了解了什么是mybatis和ORM框架,并且把mybatis的处理流程简要的概括了一下,在后面的学习中我们再针对每一个点详细解释说明。