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.youkuaiyun.com/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对象。