MyBatis是支持普通SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis消除了幾乎所有的JDBC代碼和參數的手工設置以及結果集的檢索。MyBatis使用簡單的XML或註解用於配置和原始映射,將接口和Java的POJOs(Plan Old Java Objects,普通的Java對象)映射成數據庫中的記錄。Mybatis的功能架構分爲三層(圖片借用了百度百科):
1)API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
2)數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操作。
3)基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來作爲最基礎的組件。爲上層的數據處理層提供最基礎的支撐。
如圖所示:
以在maven管理的項目架構下創建工程爲例,在pom.xml中引入相關的jar包:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.j1.mybatis</groupId>
<artifactId>mybatis-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>cn.j1.parent</groupId>
<artifactId>j1-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 單元測試 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
</dependencies>
</project>
上述依賴是調用了java後臺和MySQL數據庫連接的封裝依賴、通信和日誌記錄依賴以及mybatis封裝依賴等,接下來創建一個簡單的JDBC數據庫連接代碼package com.j1.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcTest {
public static void main(String[] args) {
// 數據庫連接
Connection con = null;
// 執行sql
ResultSet res = null;
// 封裝sql
PreparedStatement pre = null;
// 加載驅動
try {
Class.forName("com.mysql.jdbc.Driver");
// 創建連接
// 創建連接
String url = "jdbc:mysql://127.0.0.1:3306/mybatis";
String username = "root";
String password = "root";
con = DriverManager.getConnection(url, username, password);
// 獲取PreparedStatement對象
String sql = "select * from tb_user u where u.user_name=?";
pre = con.prepareStatement(sql);
// 封裝查詢的參數
pre.setString(1, "wangwu");
// 執行
res = pre.executeQuery();
// 打印結果集,
while (res.next()) {
System.out.println("username : " + res.getString("user_name"));
System.out.println("name : " + res.getString("name"));
System.out.println("age : " + res.getInt("age"));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
// 釋放資源
if (pre != null) {
pre.close();
}
if (res != null) {
res.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class.forName()函數入參是一個類,目的是要求JVM查找並加載入參指定的類,也就是說JVM會執行該類的靜態代碼段。關於forName()方法,實際上這個方法和newInstance()方法一起對類進行實例化,二者結合等同於new關鍵字。如:A
a = (A)Class.forName(“pacage.A”).newInstance(); 這和你 A a = new A(); 是一樣的效果
動態加載和創建Class
對象,比如想根據用戶輸入的字符串來創建對象時需要用到:
String str = “用戶輸入的字符串” ;
Class t = Class.forName(str);
t.newInstance();
在初始化一個類,生成一個實例的時候,newInstance()方法和new關鍵字的區別在於創建對象的方式不一樣,前者是使用類加載機制,後者是創建一個新類。而同時存在不同的對象創建方法這要考慮到軟件的可伸縮、可擴展和可重用等軟件設計思想。
java工廠模式經常使用newInstance來創建實例,如:class
c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();
其中ExampleInterface是類Example的接口,上述代碼又可以寫成如下形式:
String
className = “Example”;
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();
進一步可以寫成如下形式:
String
className = readfromXMLConfig;//從xml 配置文件中獲得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();
這裏需要說明的是,readfromXMLConfig並不是一個類,而是指通過讀取XML文件中的類配置方法從而獲取類名的抽象描述。由此,我們可以只要在目標文件中配置好繼承了ExampleInterface接口的類,上述代碼將會自動創建對應的實例。
從JVM的角度看,我們使用關鍵字new創建一個類的時候,這個類可以沒有被加載。但是使用newInstance()方法的時候,就必須保證:
1、這個類已經加載;
2、這個類已經連接了。
而完成上面兩個步驟的正是Class的靜態方法forName()所完成的,這個靜態方法調用了啓動類加載器,即加載 java API的那個加載器。現在可以看出,newInstance()實際上是把new這個方式分解爲兩步,即首先調用Class加載方法加載某個類,然後實例化。這樣分步的好處是顯而易見的。我們可以在調用class的靜態加載方法forName時獲得更好的靈活性,提供給了一種降耦的手段。
剛纔提到,Class.forName("");的作用是要求JVM查找並加載指定的類,如果在類中有靜態初始化器的話,JVM必然會執行該類的靜態代碼段。而在JDBC規範中明確要求這個Driver類必須向DriverManager註冊自己,即任何一個JDBC Driver的 Driver類的代碼都必須類似如下:
public class MyJDBCDriver implements Driver {
static {
DriverManager.registerDriver(new MyJDBCDriver());
}
}
換言之,上述forName函數入參的Driver類在之前已經在JVM中實例化並註冊了,既然在靜態初始化器的中已經進行了註冊,所以我們在使用JDBC時只需要Class.forName(XXX.XXX);就可以了。
private static final Log LOG = LogFactory.getLog(ProxoolDriver.class);
static {
try {
DriverManager.registerDriver(new ProxoolDriver());
} catch (SQLException e) {
System.out.println(e.toString());
}
}
- String sql = "select * from users where username= '"+username+"' and userpwd='"+userpwd+"'";
- stmt = conn.createStatement();
- rs = stmt.executeQuery(sql);
- String sql = "select * from users where username=? and userpwd=?";
- pstmt = conn.prepareStatement(sql);
- pstmt.setString(1, username);
- pstmt.setString(2, userpwd);
- rs = pstmt.executeQuery();
詳細講解可參看:http://blog.csdn.net/qq_20302155/article/details/73696246
package com.j1.jdbc.Model;
import java.util.Date;
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private Boolean sex;
private Date birthday;
private Date created;
private Date updated;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getSex() {
return sex;
}
public void setSex(Boolean sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getUpdated() {
return updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", password="
+ password + ", name=" + name + ", age=" + age + ", sex="
+ sex + ", birthday=" + birthday + ", created=" + created
+ ", updated=" + updated + "]";
}
}
UserMapper.xml<?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.j1.user">
<select id="queryUserByUserName" resultType="com.j1.jdbc.Model.User">
SELECT *, user_name userName FROM tb_user WHERE user_name = #{userName}
</select>
</mapper>
package com.j1;
import org.apache.ibatis.annotations.Mapper;
import com.j1.jdbc.Model.User;
public interface user{
List<User> user(String userName)
}
其中,user的入參雖然沒有必須的要求,但按習慣來講還是最好和匹配select
id下的sql語句中的關鍵字(#{}中的字段)名相符合。#jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
全局l配置文件種關於mybatis的內容mybatis_config.xml配置如下<?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>
<!-- 引入外部的配置文件 -->
<properties resource="jdbc.properties"/>
<settings>
<!-- 開啓駝峯自動映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.j1.jdbc.Model"/>
</typeAliases>
<!-- 指定環境 -->
<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>
<!-- 指定UserMapper.xml -->
<mapper resource="UserMapper.xml" />
</mappers>
</configuration>
其中,typeAliases標籤是用來設置執行文件的別名的,子標籤package用於將路徑文件夾(或包)下所有的類和接口批量設置別名,name爲文件夾或包的路徑,MyBatis默認的設置別名的方式就是去除類所在的包後的簡單的類名,比如me.gacl.domain.User這個實體類的別名就會被設置成User。當然也可以用typeAlias標籤單獨爲某個類設置別名, <typeAlias type="me.gacl.domain.User" alias="_User"/> 其中type值爲類的路徑,alias爲路徑類的別名。設置好別名後,這樣,在Mapper中我們就不用每次配置都寫類的全名了,但是namespace例外,namespace配置的時候必須寫類的全名。package com.j1.mybatis;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.j1.jdbc.Model.User;
public class Mybatis {
public static void main(String[] args) {
//讀取配置文件
try {
//配置文件
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通過SqlSessionFactoryBuilder構建一個SqlSessionFactory
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream);
//System.out.println(sqlSessionFactory);
SqlSession session=sqlSessionFactory.openSession();
//System.out.println(session);
User user=session.selectOne("com.j1.user.queryUserByUserName", "zhangsan");
System.out.println(user);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
其中,Resources類提供了從路徑讀取中加載資源的多種易於使用的方法,主要用於:1、從類路徑加載各種SQL Map配置文件;2、從類路徑加載DAO manager配置文件;3、從類路徑黏在各種properties文件。Resources中提供的加載方式有多種:1、對於簡單的只讀文本數據,加載爲Reader,匹配方法爲Reader getResourceAsReader(String resource);2、對於簡單的只讀二進制或文本數據,加載爲Stream,方法爲Stream getResourceAsStream(String
resource);3、對於可讀寫的二進制或文本文件,加載爲File,方法 File getResourceAsFile(String resource);4、對於只讀的配置屬性文件,加載爲Properties,方法爲Properties getResourceAsProperties(String
resource);5、對於只讀的通用資源,加載爲URL,方法爲Url getResourceAsUrl(String resource)。上述方法在使用時,入參的resource應當爲文件的全路徑(以文件夾/文件名的形式,上面只寫文件名的方式應該是寫錯了)其中,對於Resources類的方法也可以指定Classloader,也就是說各種方法有傳參Classloader的重載形式,如Properties getResourceAsProperties(Classloader
classloader,String resource)
1) 從核心配置文件mybatis-config.xml中獲取Environment(這裏面是數據源);
2) 從Environment中取得DataSource;
3) 從Environment中取得TransactionFactory;
4) 從DataSource裏獲取數據庫連接對象Connection;
5) 在取得的數據庫連接上創建事務對象Transaction;
6) 創建Executor對象(該對象非常重要,事實上sqlsession的所有操作都是通過它完成的);
7) 創建sqlsession對象。