Spring學習手冊(12)—— Spring JDBC數(shù)據(jù)庫訪問我們學習了如何使用Spring的JDBC抽象進行數(shù)據(jù)庫訪問,這極大的簡化了開發(fā)者的工作量:不需要關心數(shù)據(jù)庫連接、關閉;查詢語句的構造、執(zhí)行......但我們依然需要使用?
來占空查詢參數(shù)、解析查詢返回結果集。本文我們就來學習對象關系映射(Object Relational Mapping (ORM))訪問數(shù)據(jù)庫。本文主要學習當下較為流行的mybatis框架。
一、mybatis簡介
MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優(yōu)秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數(shù)以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或注解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數(shù)據(jù)庫中的記錄。
在2.0版本之前,該框架叫ibatis,后3.0版本更名為mybatis。由于Spring官方只整合了ibatis2,mybati社區(qū)為在Spring中支持mybatis,創(chuàng)建了mybatis-spring子項目使mybaits無縫的整合到Spring中去。
二、引入相關jar包依賴
Spring項目整合mybatis框架需要依賴mybatis和mybatis-spring jar包,因此我們需要首先添加該依賴。build.gradle文件依賴項修改如下:
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
compile 'mysql:mysql-connector-java:6.0.6'
compile 'org.mybatis:mybatis:3.4.2'
compile 'org.mybatis:mybatis-spring:1.3.1'
compile 'org.springframework:spring-jdbc:4.3.6.RELEASE'
compile 'org.springframework:spring-context:4.3.6.RELEASE'
}
這里mybatis版本號為3.4.2,mybatis-spring版本號為1.3.1。IDEA會自動編譯項目下載依賴jiar包,當然你也可以在項目根目錄下運行如下命令編譯(已將gradle添加到path目錄):
gradle build
三、spring整合mybatis實戰(zhàn)
我們添加了相關Jar包的依賴,本節(jié)我們就來展示如何在Spring中整合mybatis。
在開始代碼實例之前,我們首先新建數(shù)據(jù)庫score_manager(數(shù)據(jù)庫名可任意命名,但需保持dataSource中URL配置根數(shù)據(jù)庫名一致)、新建數(shù)據(jù)表student,表結構如下:
列名 | 類型 |
---|---|
id | int |
name | varchar(20) |
gender | char(1) |
age | int |
同時我們創(chuàng)建Student領域模型如下:
public class Student {
private int id;
private String name;
private String gender;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString(){
return "["+id+","+name+","+gender+","+age+"]";
}
}
3.1 創(chuàng)建SQL查詢映射文件
<?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.liangwei.learnspring.dao.StudentMapper">
<resultMap id="student" type="com.liangwei.learnspring.domain.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="gender" column="gender"/>
<result property="age" column="age"/>
</resultMap>
<select id="getStudentNums" resultType="int">
select count(*) from student
</select>
<select id="getStudentById" resultMap="student">
select * from student where id=#{studentId}
</select>
<insert id="insertStudent" parameterType="com.liangwei.learnspring.domain.Student">
insert student
(id,name,gender,age)
values(
#{id},
#{name},
#{gender},
#{age})
</insert>
<delete id="deleteStudent" parameterType="int">
delete from student where id = #{studentId}
</delete>
<update id="updateStudent" parameterType="com.liangwei.learnspring.domain.Student">
update student set gender = #{gender} where id = #{id}
</update>
</mapper>
我們使用mapper
標簽創(chuàng)建映射文件,并且使用namespace
屬性指定該mapper的命名空間,這里我們將命名空間設定為com.liangwei.learnspring.dao.StudentMapper,,之所以如此設置會在后面進行講解。
我們使用resultMap
標簽定義了一個返回結果集的映射關系,其中id
和result
內部標簽都是表示返回對象和數(shù)據(jù)庫某列的映射關系,id
指代數(shù)據(jù)庫的表主鍵,提升mybatis運行效率。property
屬性表示定義領域對象的屬性名稱,column
屬性指定了數(shù)據(jù)表中的列名。定義好resultMap
,mybatis就會自動為我們完成結果集和領域對象之間的互相轉換。
我們分別使用insert
、delete
、select
、update
來定義增刪查改語句,id
用于唯一表示該語句,parameterType
指向該語句可接受的參數(shù),resultMap
屬性設置為前面使用resultMap
標簽設置的id值,mybatis就會自動將查詢結果進行映射。
3.2 定義DataSource
<!--datasource 定義-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/score_manager"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
3.3 定義SqlSessionFactoryBean
在 MyBatis-Spring 中,我們使用SqlSessionFactoryBean來創(chuàng)建Session工廠,配置信息如下:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/>
</bean>
dataSource
屬性為前面已經(jīng)配置完成的DataSource,mapperLocations
屬性指向我們配置的SQL映射文件,這里我們設置為classpath路徑下mapper文件夾下的所有以Mapper命名結尾的XML文件。
3.4 使用SqlSessionTemplate
SqlSessionTemplate類是 MyBatis-Spring 的核心。 這個類負責管理 MyBatis 的 SqlSession, 調用 MyBatis 的 SQL 方法, 翻譯異常。 SqlSessionTemplate 是線程安全的, 可以被多個 DAO 所共享使用。
SqlSessionTemplate 實現(xiàn)了 SqlSession 接口,我們使用它來定義sqlSession,配置方式如下:
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
其中構造器參數(shù)傳入配置好的sqlSessionFactory
。
定義StudentDAO,接口定義如下:
public interface StudentDAO {
Student getStudentById(int studentId);
int getStudentNums();
void insertStudent(Student student);
void deleteStudent(int studentId);
void updateStudent(Student student);
}
接口方法命名自解釋,這里就不再對每個方法的意義進行解釋。
對StudentDAO方式實現(xiàn)如下:
public class StudentDAOImpl implements StudentDAO{
private SqlSession sqlSession;
public Student getStudentById(int studentId) {
return sqlSession.selectOne("getStudentById",studentId);
}
public int getStudentNums() {
return sqlSession.selectOne("getStudentNums");
}
public void insertStudent(Student student) {
sqlSession.insert("insertStudent",student);
}
public void deleteStudent(int studentId) {
sqlSession.delete("deleteStudent",studentId);
}
public void updateStudent(Student student) {
sqlSession.update("updateStudent",student);
}
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
}
該實現(xiàn)持有SqlSession類型的私有屬性,我們使用set方法進行注入,XML配置方式如下:
<bean id="studentDao" class="com.liangwei.learnspring.dao.impl.StudentDAOImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
以上我們使用SqlSessionTemplate完成了Spring中整合mybatis的工作,我們?yōu)槊糠N領域對象的操作定義DAO接口并實現(xiàn),我們需要為所有的DAO實現(xiàn)注入sqlSession。
3.5 使用SqlSessionDaoSupport(簡單點)
我們使用SqlSessionTemplate極大的簡化了我們對SQL的操作,但是我們依然需要為每一個實現(xiàn)類添加setter注入方法,當然我們可以定義基類然后讓所有的DAO實現(xiàn)繼承該基類,然后使用Spring提供的自動注入簡化代碼量。本節(jié)我們學習使用SqlSessionDaoSupport類,它相當于封裝了SqlSessionTemplate的基類,我們只需要使用getSqlSession()
來獲取一個SqlSessionTemplate
,剩下的操作就和SqlSessionTemplate的調用相同了。
定義StudentDAOSupport接口:
public interface StudentDAOSupport {
Student getStudentById(int studentId);
int getStudentNums();
void insertStudent(Student student);
void deleteStudent(int studentId);
void updateStudent(Student student);
StudentDAOSupport接口實現(xiàn):
public class StudentDAOSupportImpl extends SqlSessionDaoSupport implements StudentDAOSupport {
public Student getStudentById(int studentId) {
return getSqlSession().selectOne("getStudentById",studentId);
}
public int getStudentNums() {
return getSqlSession().selectOne("getStudentNums");
}
public void insertStudent(Student student) {
getSqlSession().insert("insertStudent",student);
}
public void deleteStudent(int studentId) {
getSqlSession().delete("deleteStudent",studentId);
}
public void updateStudent(Student student) {
getSqlSession().update("updateStudent",student);
}
}
XML配置提供id為studentDaoSupport的bean:
<bean id="studentDaoSupport" class="com.liangwei.learnspring.dao.impl.StudentDAOSupportImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
SqlSessionDaoSupport 需要一個 sqlSessionFactory 或 sqlSessionTemplate 屬性來 設 置 。 以上我們使用顯式的方式設置sqlSessionFactory
屬性,SqlSessionDaoSupport
會使用sqlSessionFactory
來創(chuàng)建一個SqlSessionTemplate
實例,當然我們也可以使用Spring的自動注入來省去配置代碼。 如 果 兩 者 都 被 設 置 了 , 那 么 SqlSessionFactory 是被忽略的。
3.6 注入映射器(再簡單點)
SqlSessionTemplate
和SqlSessionDaoSupport
已明顯的減少了我們關于數(shù)據(jù)操作的代碼量,而mybatis提供了映射器方式,為進一步減少代碼量提供了支持。mybatis使用代理類為我們簡化了具體的接口實現(xiàn)代碼。
定義映射器接口:
public interface StudentMapper {
Student getStudentById(int studentId);
int getStudentNums();
void insertStudent(Student student);
void deleteStudent(int studentId);
void updateStudent(Student student);
}
配置映射器,使mybatis完成映射器注入:
<bean id="studentMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.liangwei.learnspring.dao.StudentMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
我們使用org.mybatis.spring.mapper.MapperFactoryBean
定義名為studentMapper
的bean。需要為該bean提供mapperInterface
和sqlSessionFactory
屬性。sqlSessionFactory
我們已多次使用,這里就不過多贅述;mapperInterface
為我們定義的映射器接口(一般命名使用Mapper結尾),上面例子中就是我們定義的StudentMapper
接口。
這樣mybatis會自動為該映射器接口創(chuàng)建代理,并關聯(lián)相關的SQL文件定義(我們在定義sqlSessionFactory
時已經(jīng)明確制定要加載的SQL映射文件位置)。只需這樣兩步,我們完成了映射器接入工作。
??如果你對我們前面定義的SQL映射文件還有印象,應該還記得mapper
標簽下有個namespace
屬性,我們設置為com.liangwei.learnspring.dao.StudentMapper
,當時之所以如此設置就是為了使其支持映射器方式(目前SqlSessionTemplate
和SqlSessionDaoSupport
方式并沒有強制限制該namespace屬性),如果namespace屬性設置與映射器接口完全限定名不同,則會報方法為找到等類型錯誤。
更簡單點
當我們需要配置多個映射器時,按照以上方式的配置也是會花費較多的時間精力,mybatis為我們提供了更方便的方式:使用MapperScannerConfigurer配置。它會在類路徑下查找映射器,并將它們創(chuàng)建為MapperFactoryBean。
配置方式:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.liangwei.learnspring.dao" />
</bean>
這樣它會為dao包名下的所有映射器類創(chuàng)建為MapperFactoryBean。當然在現(xiàn)實項目實踐中,我們一般將包名命名為mapper結尾,如上我們可以改成com.liangwei.learnspring.dao.mapper
,這樣更方便我們閱讀并理解維護代碼。當我們需要設置多個包路徑時,我們可以使用分號或逗號進行分割。
3.7 代碼測試
以上我們使用不同的三種方式整合Spring和mybatis,我們定義了名為studentDao
、studentDaoSupport
、studentMapper
的三個bean,我們可以獲取它們并直接使用它們的方法,測試代碼如下:
public class Application {
public static void main(String[] args){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("dao.xml");
/* 獲取bean */
StudentDAO studentDAO = applicationContext.getBean("studentDao", StudentDAO.class);
/* 獲取學生數(shù)量 */
System.out.println(studentDAO.getStudentNums());
/* 查找學生ID為1 的學生信息*/
System.out.println(studentDAO.getStudentById(1));
Student student = new Student();
student.setId(3);
student.setName("Jane");
student.setGender("F");
student.setAge(20);
/* 插入新數(shù)據(jù) */
studentDAO.insertStudent(student);
/* 查詢新插入的數(shù)據(jù),ID=3 */
System.out.println(studentDAO.getStudentById(3));
student.setGender("M");
/* 更新學生數(shù)據(jù)信息,將性別改為M */
studentDAO.updateStudent(student);
System.out.println(studentDAO.getStudentById(3));
/*刪除studentID為3 的學生信息*/
studentDAO.deleteStudent(3);
/* SqlSessionDaoSupport使用 */
StudentDAOSupport studentDAOSupport = applicationContext.getBean("studentDaoSupport", StudentDAOSupport.class);
System.out.println(studentDAOSupport.getStudentNums());
System.out.println(studentDAOSupport.getStudentById(1));
student.setGender("F");
studentDAOSupport.insertStudent(student);
System.out.println(studentDAOSupport.getStudentById(3));
student.setGender("M");
studentDAOSupport.updateStudent(student);
System.out.println(studentDAOSupport.getStudentById(3));
studentDAOSupport.deleteStudent(3);
/* StudentMapper */
StudentMapper studentMapper = applicationContext.getBean("studentMapper", StudentMapper.class);
System.out.println(studentMapper.getStudentNums());
System.out.println(studentMapper.getStudentById(1));
student.setGender("F");
studentMapper.insertStudent(student);
System.out.println(studentMapper.getStudentById(3));
student.setGender("M");
studentMapper.updateStudent(student);
System.out.println(studentMapper.getStudentById(3));
studentMapper.deleteStudent(3);
}
}
如上我們對每種方式的沒種接口方法進行了調用,代碼較為簡單且結合注釋很容易理解,這里我們就不更進一步解釋了。
運行上述代碼,我們會在控制臺得到如下信息輸出:
2 //數(shù)據(jù)庫中學生個數(shù)
[1,Jone,F,20] //查詢studentId為1 的學生信息
[3,Jane,F,20] //插入studentId為3的學生信息后的查詢結果
[3,Jane,M,20]//更新了studentId為3的學生信息后的查詢結果:性別更改為M
2
[1,Jone,F,20]
[3,Jane,F,20]
[3,Jane,M,20]
2
[1,Jone,F,20]
[3,Jane,F,20]
[3,Jane,M,20]
為方便理解輸出信息,我們在輸出信息上加了些注釋,結合測試代碼和注釋可以更方便的理解輸出結果。
代碼下載
四、總結
本文我們完成了在Spring框架下mybatis的引入和整合,我們借助mybatis-spring
項目使用三種不同的方式(SqlSessionTemplate
、SqlSessionDaoSupport
和映射器
)完成整合工作。通過以上一步步的操作我們已經(jīng)體會到mybatis的簡便性、易操作性,它拆分了SQL語句和項目代碼接口的依賴,減輕繁瑣的數(shù)據(jù)庫模版代碼操作,極大的提升了程序開發(fā)人員工作效率。
完成Spring框架內整合mybatis后,我們剩下的主要工作就是定義DAO或Mapper接口,編寫映射器的XML文件,mybatis官網(wǎng)已經(jīng)給出來了較為詳細的文檔,大家可以到官網(wǎng)查看文檔學習,這里也不打算寫關于XML文件編寫方便的語法文章了。