分析流程
1、 引入dom4j
<dependencies>
<!--<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2、引入解析xml的工具类
3、引入executor类 负责执行sql语句并且封装结果集
/**
* @author 黑马程序员
* @Company http://www.ithiema.com
* 负责执行SQL语句,并且封装结果集 返回list集合
*/
public class Executor {
public <E> List<E> selectList(Mapper mapper, Connection conn) {
PreparedStatement pstm = null;
ResultSet rs = null;
try {
//1.取出mapper中的数据
String queryString = mapper.getQueryString();//select * from user
String resultType = mapper.getResultType();//com.itheima.domain.User
Class domainClass = Class.forName(resultType);
//2.获取PreparedStatement对象
pstm = conn.prepareStatement(queryString);
//3.执行SQL语句,获取结果集
rs = pstm.executeQuery();
//4.封装结果集
List<E> list = new ArrayList<E>();//定义返回值
while(rs.next()) {
//实例化要封装的实体类对象
E obj = (E)domainClass.newInstance();
//取出结果集的元信息:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//取出总列数
int columnCount = rsmd.getColumnCount();
//遍历总列数
for (int i = 1; i <= columnCount; i++) {
//获取每列的名称,列名的序号是从1开始的
String columnName = rsmd.getColumnName(i);
//根据得到列名,获取每列的值
Object columnValue = rs.getObject(columnName);
//给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
//获取它的写入方法
Method writeMethod = pd.getWriteMethod();
//把获取的列的值,给对象赋值
writeMethod.invoke(obj,columnValue);
}
//把赋好值的对象加入到集合中
list.add(obj);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
release(pstm,rs);
}
}
private void release(PreparedStatement pstm, ResultSet rs){
if(rs != null){
try {
rs.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(pstm != null){
try {
pstm.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
4、获取数据库连接
public class DataSourceUtil {
private static Connection conn;
public static Connection getConnection(Configuration cfg) throws ClassNotFoundException {
Class.forName(cfg.getDriver());
try {
conn=DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
5、编写SqlMapConfig.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 >
<!-- 配置环境-->
<environments default="mysql">
<!-- 配置mysql环境 -->
<environment id="mysql">
<transactionManager type="JDBC"/>
<!-- 配置数据源 (连接池) -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件位置 映射配置文件是指dao独立的配置文件 -->
<mappers>
<mapper resource="com/itheim/dao/IUserDao.xml"/>
<!--<mapper class="com.itheim.dao.IUserDao"/>-->
</mappers>
</configuration>
**注意**:
此处我们直接使用的是 mybatis 的配置文件,但是由于我们没有使用 mybatis 的 jar 包,所以要把配
置文件的约束删掉否则会报错(如果电脑能接入互联网,不删也行)
6、编写读取配置文件类
public class Resources {
/**
* 用于加载 xml 文件,并且得到一个流对象
* @param xmlPath
* @return
* 在实际开发中读取配置文件:
* 第一:使用类加载器。但是有要求:a 文件不能过大。 b 文件必须在类路径下(classpath)
* 第二:使用 ServletContext 的 getRealPath()
*/
public static InputStream getResourceAsStream(String xmlPath){
return Resources.class.getClassLoader().getResourceAsStream(xmlPath);
}
}
7、编写mapper类
/**
*
* <p>Title: Mapper</p>
* <p>Description: 用于封装查询时的必要信息:要执行的 SQL 语句和实体类的全限定类名</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class Mapper {
private String QueryString;//查询语句
private String ResultType;//结果类型
public String getQueryString() {
return QueryString;
}
public void setQueryString(String queryString) {
QueryString = queryString;
}
public String getResultType() {
return ResultType;
}
public void setResultType(String resultType) {
ResultType = resultType;
}
@Override
public String toString() {
return "Mapper{" +
"QueryString=\'" + QueryString + \'\\\'\' +
", ResultType=\'" + ResultType + \'\\\'\' +
\'}\';
}
}
8、编写 Configuration 配置类
/**
* 核心配置类
* 1.数据库信息
* 2.sql 的 map 集合
*/
public class Configuration {
private String Driver;
private String Url;
private String Username;
private String Password;
private Map<String, Mapper> mappers=new HashMap<String,Mapper>();
@Override
public String toString() {
return "Configuration{" +
"Driver=\'" + Driver + \'\\\'\' +
", Url=\'" + Url + \'\\\'\' +
", Username=\'" + Username + \'\\\'\' +
", Password=\'" + Password + \'\\\'\' +
", Mappers=" + mappers +
\'}\';
}
public String getDriver() {
return Driver;
}
public void setDriver(String driver) {
Driver = driver;
}
public String getUrl() {
return Url;
}
public void setUrl(String url) {
Url = url;
}
public String getUsername() {
return Username;
}
public void setUsername(String username) {
Username = username;
}
public String getPassword() {
return Password;
}
public void setPassword(String password) {
Password = password;
}
public Map<String, Mapper> getMappers() {
return mappers;
}
public void setMappers(Map<String, Mapper> mappers) {
this.mappers.putAll(mappers);
}
}
9、编写 User 实体类
/**
* 封装信息
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username=\'" + username + \'\\\'\' +
", birthday=" + birthday +
", sex=\'" + sex + \'\\\'\' +
", address=\'" + address + \'\\\'\' +
\'}\';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
基于 XML 的自定义 mybatis 框架
1、编写持久层接口和 IUserDao.xml
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
public List<User> findAll();
}
<?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.itheim.dao.IUserDao">
<select id="findAll" resultType="com.itheim.domain.User"> <!--指定包装类型 -->
select * from user;
</select>
</mapper>
**注意**:
此处我们使用的也是 mybatis 的配置文件,所以也要把约束删除了
2、编写构建者类
/**
*
* <p>Title: SqlSessionFactoryBuilder</p>
* <p>Description: 用于构建 SqlSessionFactory 的</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream in){
Configuration configuration = XMLConfigBuilder.loadConfiguration(in);
return new DefaultSqlSessionFactory(configuration);
}
}
3、编写 SqlSessionFactory 接口和实现类
/**
*
* <p>Title: SqlSessionFactory</p>
* <p>Description: SqlSessionFactory 的接口</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface SqlSessionFactory {
public SqlSession openSession() throws ClassNotFoundException;
}
/**
*
* <p>Title: DefaultSqlSessionFactory</p>
* <p>Description:SqlSessionFactory 的默认实现 </p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration cfg=null;
public DefaultSqlSessionFactory(Configuration cfg) {
this.cfg = cfg;
}
@Override
public SqlSession openSession() throws ClassNotFoundException {
return new DefaultSqlSession(cfg);
}
}
4、编写 SqlSession 接口和实现类
public interface SqlSession {
/**
* 创建dao接口的代理对象
* @param daoClass
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> daoClass);
/**
* 关闭资源
*/
public void close();
}
/**
*
* <p>Title: DefaultSqlSession</p>
* <p>Description: SqlSession 的具体实现</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class DefaultSqlSession implements SqlSession {
private Configuration cfg;
private Connection conn;
public DefaultSqlSession(Configuration cfg) throws ClassNotFoundException {
this.cfg = cfg;
conn = DataSourceUtil.getConnection(cfg);
}
@Override
public <T> T getMapper(Class<T> daoClass) {
return (T)Proxy.newProxyInstance(daoClass.getClassLoader(),new Class[]{daoClass},new MapperProxy(cfg.getMappers(),conn));
}
@Override
public void close() {
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
动态代理
/**
* 动态代理:
* 涉及的类:Proxy
* 使用的方法:newProxyInstance
* 方法的参数:
* ClassLoader:和被代理对象使用相同的类加载器,通常都是固定的
* Class[]:代理对象和被代理对象要求有相同的行为。(具有相同的方法)
* InvocationHandler:如何代理。需要我们自己提供的增强部分的代码
*/
public class MapperProxy implements InvocationHandler {
private Map<String,Mapper> mappers;
private Connection conn;
public MapperProxy(Map<String, Mapper> mappers, Connection conn) {
this.mappers=mappers;
this.conn=conn;
}
/**
* 用于对方法增强 调用selectlist
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName(); //获取方法名
String className = method.getDeclaringClass().getName();//获取方法所在类的名
//组合key
String key=className+"."+methodName;
System.out.println(key);
//4.使用 key 取出 mapper
Mapper mapper = mappers.get(key);
Executor executor = new Executor();
//5.判断是否有mapper
if(mapper == null){
throw new IllegalArgumentException("传入的参数有误");
}
return executor.selectList(mapper,conn);
}
}
5、运行测试类
public class UserDao{
public static void main(String[] args) throws IOException, ClassNotFoundException {
//读取配置文件
InputStream in = Resources.getResourceAsStream("mybatisConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//使用工厂生产Sqlsession对象
SqlSession session = factory.openSession();
//使用Sqlsession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//实现类的方法
// IUserDao userDao=new UserDaoImp(factory);
List<User> users=userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//释放资源
// session.close();
in.close();
}
}
基于注解方式定义 Mybatis 框架
自定义@Select 注解
/**
* <p>Title: Select</p>
* <p>Description: 自定义查询注解</p>
* <p>Company: http://www.itheima.com/ </p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String value();
}