搭建MyBatis数据库工作环境

JDBC的“硬编码”

万事开头难,搭建MyBatis时,各种配置文件,映射关系,虽然一时间不好理解,但是搭建好整个框架后,可以大幅度提高开发效率。和传统的JDBC相比,MyBatis一个比较大的优点是减少了“硬编码”。硬编码就是把一些外部数据或程序运行时生成的数据,用赋值语句写死在了源代码中,这样做的坏处是,如果我们想修改一些字段或数据,我们必须从源代码处修改,修改完后重新编译执行,这样的方式明显降低了程序的可扩展性。JDBC的一个缺点就是在于数据库驱动程序,连接地址等采用“硬编码”的方式,导致如更换数据库后,不利于维护的问题。例如我们看JDBC的连接数据库方式:

public Connection connectDataBase() {
		//数据库连接对象
		Connection con = null;
		//驱动程序
        String driver = "com.mysql.jdbc.Driver";
        
        //databaseName指向要访问的数据库名
        String url = "jdbc:mysql://localhost:3306/User?useSSL=false";
        //数据库连接用户名
        String user = "root";
        //数据库连接密码
        String password = "12345";
        
        try {
            Class.forName(driver);
            con = DriverManager.getConnection(url,user,password);
            //判断连接是否成功
            if(!con.isClosed()) {
            	System.out.println("数据库连接成功!");
            }
        } catch(ClassNotFoundException e) {
        	e.printStackTrace();
        } catch(SQLException e) {
        	e.printStackTrace();
        }
        //最后返回数据库连接对象
        return con;
	}

JDBC的数据库连接,把驱动程序名、数据库连接地址,数据库用户和密码等数据都是通过赋值语句写死在程序代码中的,假如我们对程序维护时,遇到如数据库账户或密码等修改,就要对数据库连接源代码进行修改,然后重新编译,最后再运行,这种方式工作量大,不利于维护。还有一点是每一个需要连接数据库的类中,都要引入这个数据库连接类,实例化对象,调用连接方法,使用完后等对象被自动回收,容易造成资源的浪费。

      JDBC还有一个缺点就是它的数据库操作语句

      对于JDBC的“硬编码”问题,MyBatis的解决方式是把这些数据库配置信息,写入在XML文件中,同样地,SQL语句也是配置在XML文件里,而不是直接编写在Java类中。那么执行完的SQL语句,获得的结果集怎么取得?得益于MyBatis的输出映射,能够将SQL检索得到的结果集映射成一个Java对象,然后从这个对象中获取数据,详细的接下来说。

MyBatis框架各部分

一个MyBatis框架由6部分组成:分别是数据源配置文件、SQL映射配置文件、会话工厂、会话、执行器和底层对象。首先来看最重要的数据源配置部分:

数据源配置文件

为什么说这部分是最重要的?因为数据源配置文件存放了数据库连接的信息,例如数据库驱动程序、数据库连接地址、数据库账户和密码等,它是一个全局配置文件。看图:

第一行指定xml版本信息为“1.0”和编码方式“UTF-8”。第2-4行中,从第3行的“DTD(Document Type Definition)”可以知道,这是配置文档类型定义,也就是按照某种方式来检查配置文件中的标签和标签中的参数是否正确。这里引入的是MyBatis的DTD文档规范标准。

      接着第6-9行settings配置标签对中可以设置logImpl,即选择哪种日志工具输出日志信息,因为我使用的是mybatis-3.4.1,所以配置Log4j。第10-25行,配置环境信息,即数据库连接信息部分,这部分很重要,一起来看,MyBatis的环境信息配置在environments标签对中,里面又有environment标签对,每一个environment标签对表示一个独立的数据库,所以我们可以在里面配置多个数据库信息。首先来看第14行,transactionManager标签配置事务控制类型为JDBC机制。第16行dataSource标签配置数据库的连接信息,type=“POOLED”意思数据源类型,dataSource的数据源类型有三种:UNPOOLED、POOLED和JNDI,分别对应的是“不适用连接池”、“使用连接池”和“Java命名与目录接口”。

      第18-23行,配置要连接的数据库信息,熟悉的数据库驱动程序,连接地址等信息。最后第28行,配置Mapper映射文件的路径,Mapper.xml文件中配置的是SQL语句,如下面的UserMapper.xml中我们会看到。

      看到这里,你可能会想,在这个数据源配置文件中,数据库驱动,连接地址,账户和密码不也是“硬编码”上去的吗?没错,在搭建一个最简单的MyBatis时,是这样,不过我们通常把数据库的这些连接信息配置到一个properties文件中,像这样:

# JDBC数据库配置信息
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8&useSSL=false
username = root
password = 12345

然后在数据源配置文件中,引入这个properties文件:

<?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="db.properties"></properties>
	<settings>
		<!-- 配置日志输出模式logImpl为LOG4J -->
		<setting name="logImpl" value="LOG4J" />
	</settings>
	<!-- 配置环境信息 -->
	<environments default="development">
		<environment id="development">
			<!-- 使用JDBC事务管理 -->
			<transactionManager type="JDBC" />
			<!-- 数据库连接池设置 -->
			<dataSource type="POOLED">
				<!-- 数据库驱动 -->
				<property name="driver" value="${driver}" />
				<!-- 数据库连接地址 -->
				<property name="url" value="${url}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
<mappers>
	<!-- Mapper映射文件的声明,配置Mapper.xml的路径,Mapper.xml文件中配置SQL语句,输入参数和输出结果类型等信息 -->
    <mapper resource="sqlmap/UserMapper.xml"/>
</mappers>
</configuration>

第6行,引入数据库信息properties文件后,就可以在其他位置通过占位符“${ }”来获得相应的值,例如看下面配置数据库驱动,value = “${driver}”,driver就是db.properties文件中的driver,使用这样的方法,就可以避免“硬编码”,在需要操作不同的数据库时,只要选择不同的数据库properties配置文件即可,这也是常用的方法。

在上面我们还看到一个词:”数据库连接池“,什么是数据库连接池?

 

数据库连接池

回想一下之前的线程池,为什么需要线程池?原因是,虽然多线程的设计方式可以充分地利用多核处理器,但是如果线程数量过大,或者频繁地创建和回收线程,系统的性能反而会下降。为了解决这些问题,线程池出现了,它的功能是线程复用,线程池中始终存在几个活跃的线程,当程序需要时,就会从线程池中拿出一个线程,使用完后,线程不会被释放,而是交还给线程池,等待下一次被拿出来使用。

数据库连接池的存在就是为了解决类似的问题。假设每次程序需要连接数据库时,都要去建立新的连接对象,使用完数据库后销毁连接对象,这种频繁新建连接对象和销毁连接对象的情况也是会浪费许多资源和时间的。使用数据库连接池,池中始终有一些保持连接状态的数据库连接,当程序需要使用数据库连接时,从数据库连接池中返回一个可用的连接,使用完毕后,该连接不会被关闭,而是返回到数据库连接池中,等待下一次被分配。

 

日志输出配置文件

数据库连接信息配置文件弄好后,接下来应该就是组织SQL语句了,不过既然我们要输出日志信息,那么就先来把这个日志工具也配置好它吧,毕竟日志信息太重要了,在调试阶段帮了我大忙。mybatis-3.4.1中包含了log4j日志输出工具,那么我们就直接配置使用它吧:

# Global logging configuration
# 配置日志的输出级别
log4j.rootLogger=DEBUG, stdout

# 配置stdout的输出端载体类型是控制台类型
log4j.appender.stdout=org.apache.log4j.ConsoleAppender

# 配置stdout的输出端载体的布局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# 配置日志输出信息的格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

 

SQL映射配置文件

SQL映射配置文件,就是Mapper配置文件,里面存放了SQL语句,也就是增删查改,在Mapper文件中,可以设置SQL语句的参数类型和查询语句返回的结果集对象,这里的结果集对象很重要,它能把数据库操作返回的结果集映射为Java对象,之后我们就可以通过对象的get()方法获得想要数据,而不是像JDBC那样遍历结果集。来看看它的配置:

<?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="SQLtest">
    <select id="findUserById" parameterType="int" resultType="com.mybatis.po.User">
        SELECT * FROM USER WHERE id=#{id}
    </select>
    
    <insert id="insertUser" parameterType="com.mybatis.po.User">
    	INSERT INTO USER(username, password, gender, birthday, email, province, city)
    	VALUE(#{username}, #{password}, #{gender}, #{birthday,jdbcType=DATE}, #{email}, #{province},
    	#{city})
    </insert>
    
    <delete id="deleteUser" parameterType="java.lang.String">
    	DELETE FROM USER WHERE username=#{value}
    </delete>
    
    <update id="updateUser" parameterType="com.mybatis.po.User">
    	UPDATE USER SET gender=#{gender} WHERE id=#{id}
    </update>
    
    <select id="multiSearch" resultType="com.mybatis.po.User">
    	SELECT * FROM USER
    </select>
</mapper>

SQL语句标签都包含在mapper标签中,可以看到,下面的增删查改语句都包含在mapper标签中,每一个标签对中包含一段SQL语句,其中id是标识,parameterType表示输入参数类型,例如下面这句:“SELECT * FROM USER WHERE id=#{id}”中,指定参数id的类型是int型。ResultType指定了数据库操作返回的结果集所映射的Java对象。mapper标签可以有多个,其中有一个namespace属性,来标识不同的mapper,例如你可以把所有的查询功能语句放到一个mapper中,所有的更新功能语句放到另一个mapper中,以此来分类实现不同业务的数据库操作。

      到这里,数据库部分的配置都弄好了,数据源文件,日志文件(不是必须),SQL映射文件,接下来,到编写测试数据库是否正确配置并能正确工作的Java类

 

编写Java类

Java类中包含有:

  1. 类中字段与数据库表中的字段一一对应的Java类,称为持久化实体类,每一个持久化实体类表示数据库中一条记录。
  2. 数据库交互类,用来创建一个可以与数据库交互的SqlSession类,下面再详细说。
  3. 数据库测试类,就是对数据库进行增删查改操作。

那么就来编写这最后一部分内容吧。

 

持久化实体类

正如上面所说,持久化实体类就是类中的字段和数据库中的字段一一对应,那么数据库中有多少列(字段),类中就对应有多少个字段。通常持久化实体类是一个JavaBean(即满足有一个空的构造方法,可序列化,以及类中属性都有对应的get()和set()方法):

package com.mybatis.po;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int id;
	private String username;
	private String password;
	private String gender;
	private String email;
	private String province;
	private String city;
	private Date birthday;
	
	public User() {
		
	}
	// 构造方法初始化
	public User(int id, String username, String password, String gender, String email, String province,
			String city, Date birthday) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
		this.gender = gender;
		this.email = email;
		this.province = province;
		this.birthday = birthday;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	
    .................. //下面的get()/set()方法相同,不一一列出
}

每一个字段都有对应的get()方法,return自己出去,以及一个set()方法来设置自己的值,下面的字段get()/set()方法一样,就不全部写出来了。

 

数据库交互类

数据库交互类十分重要!里面的SqlSessionFactory会话工厂类会去读取,根据你在数据源配置文件(Resources)中的配置信息,以及SQL映射文件Mapper.xml(它们的路径配置在数据源配置文件中),创建一个可以与你配置的数据库交互的SqlSession会话实例类,通过这个类实例化的对象,你才可以对数据库进行增删查改等操作。先来看看它的实现:

package com.mybatis.datasource;

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;

public class DataConnection {
	private String resource = "SqlMapConfig.xml"; //数据源配置文件
	private SqlSessionFactory sqlSessionFactory; //会话工厂
	private SqlSession sqlSession;
	
	public SqlSession getSqlSession() throws IOException {
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 创建会话工厂,传入MyBatis配置文件信息
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		sqlSession = sqlSessionFactory.openSession();
		
		return sqlSession;
	}
}

第16行,使用资源加载类Resources来加载我们的数据源配置文件(也就是第11行的SqlMapConfig.xml,里面配有数据库驱动程序、;连接地址、账户和密码,以及SQL映射文件等)。然后就可以根据获得的配置信息创建能与这个数据库交互的SQL会话工厂,最后第19行获得这个SQL会话工厂的实例,返回出去后,就可以通过这个sqlSession实例来操作这个数据库了。具体怎么使用这个实例,来看看最后的数据库测试类:

 

数据库测试类

在数据库测试类中,自然是对数据库进行增删查改操作了,测试下我们前面配置了那么多的信息,最终能否正确连上数据库,并对它进行操作。先来看查找操作:

public void TestSelect() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		User user = sqlSession.selectOne("SQLtest.findUserById", 1);
		StringBuffer result = new StringBuffer();
		result.append("用户名: " + user.getUsername() + "\r\n");
		result.append("密码: " + user.getPassword() + "\r\n");
		result.append("性别: " + user.getGender() + "\r\n");
		result.append("电子邮箱: " + user.getEmail() + "\r\n");
		result.append("省份: " + user.getProvince() + "\r\n");
		result.append("城市: " + user.getCity() + "\r\n");
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		result.append("出生日期: " + sdf.format(user.getbirthday()) + "\r\n");
		
		System.out.println(result.toString());
		sqlSession.close();
	}

无论是增删查改操作,我们都是首先获得能操作这个数据库的SqlSession会话实例,这一步通过上面的数据库交互类实现。之后我们就可以通过这个会话对象调用相应的方法来操作数据库,例如查找,看到第15行调用了sqlSession.selectOne()方法来进行查找操作,第一个参数是我们的SQL映射配置文件中的mapper标签,上面说了,mapper标签可以有多个,它们通过namespace来标识彼此之间的不同,这里selectOne()方法使用了那么,SQLtest中findUserById标签里的SQL语句,第二个参数是执行SQL的传入参数,指查询id为“1”的用户记录,返回的是一个User对象,这些都是在SQL映射配置文件里约定好的。SQL执行完后返回的结果集被映射成了User对象,之后我们通过这个对象的get()方法来获得查询得到的各个字段的值,最后输出:

从日志信息中可以看到Preparing:SELECT *FROM USER WHERE id=?

是我们执行的SQL语句,Parameters:1(Integer)是传入的参数,最后返回一条记录。我们还留意到。日志信息第7行中我们得到数据库连接,查询完成后,最后我们会把这个连接返回到连接池中,这也是因为我们在数据源配置文件中使用了数据库连接池。

      这里我们使用的是selectOne()方法,因为我们只查询一个用户的记录,如果要查询多个或者全部用户,则可以使用selectList()方法,查询结果返回一个List集合,然后我们可以遍历这个集合来获取所有查询的记录,例如我们来查询所有的用户信息:

public void TestMultiSelect() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		List<User> resultList = sqlSession.selectList("SQLtest.multiSearch");
		StringBuffer result = new StringBuffer();
		
		for(int i=0; i<resultList.size(); i++) {
			User user = resultList.get(i);
			result.append("用户名: " + user.getUsername() + "\r\n");
			result.append("密码: " + user.getPassword() + "\r\n");
			result.append("性别: " + user.getGender() + "\r\n");
			result.append("电子邮箱: " + user.getEmail() + "\r\n");
			result.append("省份: " + user.getProvince() + "\r\n");
			result.append("城市: " + user.getCity() + "\r\n");
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			result.append("出生日期: " + sdf.format(user.getbirthday()) + "\r\n");
			
			System.out.println(result.toString());
			result.setLength(0);
		}
		
		sqlSession.close();
	}

查询结果:

从日志信息中可以看到,最终返回了8条记录。

至于删除,修改和插入操作,相信你很容易就会看明白:

public void TestDelete() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		sqlSession.delete("SQLtest.deleteUser", "测试用户");
		sqlSession.commit();
		sqlSession.close();
	}
	
	public void TestUpdate() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		User user = new User();
		user.setId(7);
		user.setGender("女");
		
		sqlSession.update("SQLtest.updateUser", user);
		sqlSession.commit();
		sqlSession.close();
	}
	
	public void TestInsert() throws IOException, ParseException {
		SqlSession sqlSession = dataConn.getSqlSession();
		User user = new User();
		user.setUsername("杨丽");
		user.setPassword("666");
		user.setGender("女");
		user.setEmail("[email protected]");
		user.setProvince("广东省");
		user.setCity("广州市");
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		user.setbirthday(sdf.parse("1999-03-18"));
		
		sqlSession.insert("SQLtest.insertUser", user);
		sqlSession.commit();
		sqlSession.close();
	}

与查找不同的是,它们最后要调用commit()方法来提交操作。执行后都会在日志信息中输出返回的行数,大家可以自行查看。

 

完整代码已上传GitHub:

https://github.com/justinzengtm/SSM-Frame

发布了97 篇原创文章 · 获赞 71 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/justinzengTM/article/details/94971586