Java EE--框架篇(3-1)Mybatis
目錄
前言
帶著問題學java系列博文之java基礎篇。從問題出發,學習java知識。
Hibernate/Mybatis
前面無框架開發後臺服務時(《Java EE--無框架開發後臺服務》),資料庫操作這塊,沒有藉助任何框架,直接就是JDBC程式設計,整個需求實現下來可以說是痛苦萬分。
1.每次運算元據庫都要建立資料庫連線,執行操作後又要釋放連線,存在頻繁的資料庫連線建立、銷燬;造成資源消耗,效率低下;
2.所有的操作都需要編寫sql語句,並完成引數和sql語句的拼接,沒有與實體類物件關聯起來,操作複雜;
3.查詢之後,需要遍歷ResultSet結果集,完成結果集與物件實體的封裝;
4.對於查詢結果集、重複查詢等沒有任何快取,每一次都需要從資料庫查詢資料;
5.不支援自動建表、更新表。
上面總結的痛點相信經歷過純手工JDBC程式設計的都深有體會,為了解決這些問題,所以推出了ORM(Object relationship mapping 物件關係對映)框架,旨在採用池技術統一管理資料庫連線session、connection;自動封裝物件實體類與資料表之間的關聯(實體類與資料表關聯,查詢結果自動封裝為具體物件);對查詢結果可配置快取(一級、二級快取),對於重複查詢可以從快取中獲取,提高效率;全自動的ORM框架還支援自動建表、更新表等。簡單的說,有了ORM框架,將會發現資料庫程式設計這塊會徹底解放,甚至都不需要程式設計師去編寫sql,一切都由框架準備好了。
下面就帶領大家快速上手這兩大框架,也經典的後臺架構SSM/SSH中的第三個框架。(本博文都是基於SpringBoot(SpringBoot框架可以看成是Spring SpringMVC集合,後續博文會單獨講解),以全註解舉例。如果想要詳細學習框架,建議還是從配置階段開始,逐漸過渡到全註解階段。當然如果只是為了使用它,那直接學習全註解,能上手開發即可)
快速入門Mybatis
建表語句:
CREATE TABLE `school`.`student` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '學生編號',
`name` VARCHAR(45) NOT NULL COMMENT '姓名',
`age` INT(11) NOT NULL COMMENT '年齡',
`address` VARCHAR(100) NULL COMMENT '居住地址',
PRIMARY KEY (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '學生表';
CREATE TABLE `school`.`grades` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`student_id` INT NOT NULL COMMENT '外來鍵關聯學生表',
`math` INT NOT NULL COMMENT '數學成績',
`chinese` INT NOT NULL COMMENT '語文成績',
`english` INT NOT NULL COMMENT '英語成績',
PRIMARY KEY (`id`),
INDEX `student_id_idx` (`student_id` ASC) VISIBLE,
CONSTRAINT `studentId`
FOREIGN KEY (`student_id`)
REFERENCES `school`.`student` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '成績表';
整合對接:
專案結構圖:
1.實體類
@Data
@Table(name = "grades")
public class Grades implements Serializable {
@Id
@KeySql(useGeneratedKeys = true)
private Integer id;
@Column(name = "student_id")
private Integer studentId;
private Integer math;
private Integer chinese;
private Integer english;
}
@Data
@Table(name = "student")
public class Student {
@Id
@KeySql(useGeneratedKeys = true) //mybatis通用mapper的註解,表明自增屬性
private Integer id;
private String name;
private Integer age;
private String address;
}
2.mapper介面(依賴通用mapper實現)
public interface StudentMapper extends Mapper<Student> {
}
public interface GradesMapper extends Mapper<Grades> {
}
3.啟動類新增註解掃描mapper
@SpringBootApplication
@MapperScan("com.zst.learnmybatis.mapper") //配置要掃描的mapper包名
public class LearnmybatisApplication {
public static void main(String[] args) {
SpringApplication.run(LearnmybatisApplication.class, args);
}
}
4.配置資訊
##日誌中列印具體的sql語句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
##指定掃描的實體類包名
mybatis.type-aliases-package=com.zst.learnmybatis.domain
##使用druid資料庫連線池
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/school?useSSL=true&characterEncoding=utf-8&serverTimezone=GMT
spring.datasource.druid.username=root
spring.datasource.druid.password=***
5.所需依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
至此,springboot整合Mybatis(依賴通用mapper啟動器)完畢,這裡用了druid連線池,tk.mybatis.mapper。用通用mapper的目的,是為了簡化編碼,因為通用mapper基本實現了單表操作的所有方法,可以直接呼叫。
單表增刪改查
@SpringBootTest
@RunWith(SpringRunner.class)
class StudentMapperTest {
@Autowired
StudentMapper studentMapper;
//單表增刪改查
@Test
public void testInsert(){
Student student = new Student();
student.setName("王五");
student.setAge(18);
student.setAddress("合肥");
studentMapper.insert(student);
}
}
單表增刪改查等基本操作,可以直接使用通用mapper的基礎方法,是不是非常簡便。我們什麼都沒做,但是框架已經為我們實現所有!
下面梳理一下mapper中基本的增刪改查方法:
新增方法:
mapper給我們預設實現了兩個方法:insert()和insertSelective();它們分別的意思是:
insert():表示向表中插入一條資料,表列值就是該物件的屬性值。如果有自增主鍵,且物件例項的主鍵屬性也有值,則直接插入,如果物件主鍵屬性沒有值,則由資料庫根據表資料自動生成。
insertSelective():區別insert(),這個方法僅給屬性值不為null的列設定值,如果物件例項屬性為null或者未設定值,則不做任何操作,表資料該列值由資料庫預設填充。
刪除方法:
mapper給我們實現了三個刪除方法:deleteByPrimaryKey()、delete()、deleteByExample()
deleteByPrimaryKey():這個方法就如其名,根據主鍵刪除;
delete():這個方法相當於條件刪除,即刪除表記錄中列值符合物件例項屬性值的所有記錄(物件例項的屬性值相當於and拼接的條件);
deleteByExample():這個方法完全就是條件刪除,其中傳入的example就是mybatis定義的專門用於條件拼接的類。
修改方法:
mapper給我們實現了四個修改方法:updateByPrimaryKey()、updateByExample()、updateByExampleSelective()、updateByPrimaryKeySelective()
updateByPrimaryKey():根據主鍵更新表記錄,實體屬性空值也會設定到資料表對應列;
updateByPrimaryKeySelective():根據主鍵更新表記錄,實體屬性空值也不會設定到資料表對應列,對應列的值由資料庫已有值或者預設填充;
updateByExample():根據條件更新,相當於更新所有符合條件的表記錄,實體屬性值對應表的列,空值也會設定;
updateByExampleSelective():根據條件更新,相當於更新所有符合條件的表記錄,實體屬性值對應表的列,空值不會設定,由資料庫已有值或者預設填充;
查詢方法:
查詢方法內建有很多個,分別解釋一下意思:
select():將傳入的實體類屬性值作為條件,進行查詢;
selectAll():查詢表中所有記錄;
selectByExample():根據傳入的Example進行條件查詢;
selectByRowBounds():將傳入的實體類屬性值作為條件,拼接上RowBounds的取結果限制(分頁),進行查詢;這個方法就是為了分頁準備的,RowBounds支援兩個引數offset和limit;
selectByExampleAndRowBounds():將傳入的Example作為條件,拼接上RowBounds的取結果限制(分頁),進行查詢;(分頁方法)
selectByPrimaryKey():根據主鍵查詢;
selectCount():查詢記錄總數;
selectCountByExample():根據example條件查詢,查詢記錄總數;
selectOne():將傳入的實體類屬性值作為條件,進行查詢;必須確保符合條件的記錄僅有一條,否則將會報錯;
selectOneByExample():將傳入的Example作為條件,進行查詢,必須確保符合條件的記錄僅有一條,否則將會報錯;
單表條件查詢
@SpringBootTest
@RunWith(SpringRunner.class)
public class StudentMapperTest {
//單表條件查詢
@Test
public void testExample(){
Example example = new Example(Student.class);
example.createCriteria().andLike("name","%二%")
.orLike("address","%肥%");
List<Student> students = studentMapper.selectByExample(example);
for (Student student : students) {
System.out.println(student);
}
}
}
如上,使用Example物件,拼接條件,然後進行條件查詢。上例演示了查詢姓名中含有“二”或者地址中含有“肥”的學生。
呼叫Example物件的createCriteria()方法,建立一個條件物件後,就可以呼叫相關方法,進行條件拼接。Example.Criteria物件具體方法如下:
addCriterion()、addOrCriterion() :新增自定義的條件(or 等同於 sql語句的or:或運算連線)
andIsNull()、andIsNotNull():值是空/非空(and 等同於 sql語句的and:與運算連線;類似方法,or連線符)
andEuqalTo()、andNotEqualTo():相等/不相等(類似方法,or連線符)
andLessThan()、andLessThanOrEqualTo():小於/小於等於(類似方法,or連線符)
andIn()、andNotIn():在取值範圍內/不在範圍內(類似方法,or連線符)
andBetween()、andNotBetween():在取值範圍內/不在範圍內(類似方法,or連線符)
andLike()、andNotLike():含有/不含有條件值(類似方法,or連線符)
也可以直接用Example的方法進行條件拼接:
@SpringBootTest
@RunWith(SpringRunner.class)
public class StudentMapperTest {
//單表條件查詢
@Test
public void testExample(){
Example example = Example.builder(Student.class).andWhere(Sqls.custom().andLike("name","%二%")
.orLike("address","%肥%")).orderByDesc("id").build();
studentMapper.selectByExample(example);
}
}
如上還加上了排序,演示了查詢姓名中含有“二”或者地址中含有“肥”的學生,並按照id降序輸出。
單表條件查詢Example中的方法涵蓋了所有情形嗎?如果有些沒有涵蓋怎麼辦呢?
其實單表查詢,使用Example基本可以滿足所有場景了。如果真的遇到Example或者Example.Criteria中沒有的,也可以通過手寫sql來實現,範例如下:
public interface StudentMapper extends Mapper<Student> {
@Select("select * from student where name like #{name} or address like #{address} order by id desc")
List<Student> likeNameOrAddressOrderByIdDesc(String name,String address);
}
即只需要在Mapper介面中增加方法,編寫sql語句就好。上例的方法和使用Example查詢效果完全一致。
***:#{param}和${param}的區別是?
上例編寫sql語句時,引數設定這塊用了一個符號表示式#{name},它的意思是這裡使用引數name。而mybatis還有另外一種符號表示式:${param}
如果上例使用這個表示式的話,具體如下:
select * from student where name like '%${name}%' or address like '%${address}%' order by id desc
可以看到${param}表示式,相當於字串佔位符,僅僅是將引數作為字串填充進sql語句。
從這兩個sql語句可以看出來,很明顯使用#{param}更好一點,原因很簡單:使用#{param}相當於使用了PrepareStatement,向其中新增引數,執行效率更高,而且沒有sql注入漏洞;而使用${param}相當於使用了statement,只是一個字串填充,執行效率低,且存在sql注入漏洞。因此推薦使用#{param}表示式,不過要注意,像本例中like查詢的時候,需要對傳參name手動加上“%%”,然後再作為方法傳參。
***:查詢結果是自定義型別,怎麼辦?
mapper中自帶的方法返回型別都是實體類或者實體類集合等,如果我們要求返回自定義型別,該怎麼辦呢?顯然此時就無法使用mapper自帶的方法了,需要我們在mapper介面類中編寫sql,自定義返回型別,如下例:
public interface StudentMapper extends Mapper<Student> {
@Select("select name,age,address from student where id = #{id}")
@ResultType(StudentDto.class) //可以使用註解表明返回型別,也可以省略不寫
StudentDto queryByPrimaryKey(Integer id);
}
/**
* 自定義返回型別實體類
* 注意屬性名要和查詢語句返回的列名一一對應,否則將無法轉化
* 本質:框架替我們封裝了返回結果而已
* ResultSet.getString("name")->name屬性值
* ResultSet.getString("address")->address屬性值
* ResultSet.getInt("age")->age屬性值
*/
@Data
@ToString
public class StudentDto {
private String name;
private Integer age;
private String address;
}
@SpringBootTest
@RunWith(SpringRunner.class)
public class StudentMapperTest {
//自定義型別
@Test
public void testDefine(){
StudentDto studentDto = studentMapper.queryByPrimaryKey(1);
System.out.println(studentDto.toString());
}
}
多表聯合查詢
通用mapper只有當前類對應的單表操作系列方法,如果要實現多表聯合查詢,則必須在mapper介面類中增加相應方法,編寫sql語句。如下例:
@Data
@ToString
public class StudentScoreDto {
private String name;
private Integer math;
private Integer chinese;
private Integer english;
}
public interface StudentMapper extends Mapper<Student> {
@Select("select s.name,g.math,g.chinese,g.english from student s left join grades g on s.id = g.student_id where s.id = #{id}")
StudentScoreDto queryStudentScoreById(Integer studentId);
}
@SpringBootTest
@RunWith(SpringRunner.class)
public class StudentMapperTest {
//多表查詢
@Test
public void testMulti(){
StudentScoreDto studentScoreDto = studentMapper.queryStudentScoreById(2);
System.out.println(studentScoreDto.toString());
}
}
本例演示了查詢id為2的學生的姓名以及各科的分數,用到了自定義型別,以及編寫sql語句(左連查詢)。
分頁查詢
上面單表查詢時分析過,mapper給我們寫好了方法,支援分頁查詢,如下例:
@SpringBootTest
@RunWith(SpringRunner.class)
public class StudentMapperTest {
//分頁查詢
@Test
public void testPageQuery(){
List<Student> students = studentMapper.selectByRowBounds(null, new RowBounds(3, 5));
for (Student student : students) {
System.out.println(student);
}
}
}
從上圖可以看到其實還是查詢了所有資料,只是在返回資料時,框架給我們做了資料篩除。其它的藉助RowBounds物件實現分頁查詢方法也類似。另外使用這個方法有個缺陷,那就是需要我們每次計算出offset(偏移量),然後作為引數出入方法。顯然,這種查詢效率低,還需要我們每次都計算偏移量的分頁方式很不友好。為此,業內出了各種幫助分頁查詢的小外掛框架,比較著名的有PageHelper,下面以PageHelper舉例:
@SpringBootTest
@RunWith(SpringRunner.class)
public class StudentMapperTest {
//分頁查詢
@Test
public void testPageQueryByHelper(){
//開啟分頁
PageHelper.startPage(2,5);
List<Student> students = studentMapper.selectAll();
PageInfo<Student> pageInfo = new PageInfo<>(students);
for (Student student : students) {
System.out.println(student);
}
System.out.format("total:%s,pageSize:%s,pageNum:%s",pageInfo.getTotal(),pageInfo.getPageSize(),pageInfo.getPageNum());
System.out.println();
for (Student student : pageInfo.getList()) {
System.out.println(student);
}
}
}
可以看到,使用PageHelper進行分頁查詢,雖然看起來還是呼叫mapper的selectAll()查詢所有,但其實是由PageHelper接管了sql語句,分別執行了兩次sql查詢,首先查詢出記錄總數,然後PageHelper會根據傳入的引數(PageNum 頁碼,PageSize 每頁記錄數)計算出偏移量,作為sql查詢 limit 語句後的引數填入。進行第二次查詢,獲得分頁資料。PageHelper雖然分為兩次查詢,但是兩次都是小資料量返回,對於表資料非常多的情形,很明顯比一次查詢所有記錄效率要高得多,且佔用記憶體也更小。因此推薦使用PageHelper進行分頁查詢,不建議使用mapper自帶的方法。
以上繫個人理解,如果存在錯誤,歡迎大家指正。原創不易,轉載請註明出處!
相關文章
- java框架之mybatisJava框架MyBatis
- java面試-mybatis篇Java面試MyBatis
- mybatis系列第1篇:框架原理MyBatis框架
- Java Mybatis 框架入門教程JavaMyBatis框架
- 446、Java框架100 -【MyBatis - if】 2020.12.23Java框架MyBatis
- Java 持久層框架之 MyBatisJava框架MyBatis
- Java持久層框架Mybatis入門Java框架MyBatis
- 關於MyBatis框架這一篇就夠了MyBatis框架
- Mybatis框架MyBatis框架
- Java面試題-框架篇八Java面試題框架
- Java面試題-框架篇九Java面試題框架
- 框架系列——MyBatis框架MyBatis
- java框架整合Springmvc+mybatis+shiro+lucene+rest+webservice+mavenJava框架SpringMVCMyBatisRESTWebMaven
- Java面試題-集合框架篇三Java面試題框架
- 手寫mybatis框架MyBatis框架
- 初識MyBatis框架MyBatis框架
- 快速搭建MyBatis 框架MyBatis框架
- Java程式設計——如何用Maven搭建SpringMVC+Spring+MyBatis框架Java程式設計MavenSpringMVCMyBatis框架
- Mybatis框架基礎-03MyBatis框架
- Java 集合框架綜述,這篇讓你吃透!Java框架
- 3-1 名稱空間
- Java開發工程師進階篇——深入淺出Java集合框架Java工程師框架
- MyBatis框架原理3:快取MyBatis框架快取
- 搭建 Spring+SpringMVC+MyBatis 框架SpringMVCMyBatis框架
- Mybatis框架 入門學習MyBatis框架
- Spring+SpringMVC+MyBatis框架整合SpringMVCMyBatis框架
- MyBatis-Plus——實踐篇MyBatis
- MyBatis-Pro,新一代的MyBatis增強框架MyBatis框架
- Java個人技術知識點總結(框架篇)Java框架
- MyBatis框架介紹及其實操MyBatis框架
- springboot整合mybatis自動生成框架Spring BootMyBatis框架
- MyBatis 框架之快速入門程式MyBatis框架
- 手寫自己的MyBatis框架-SqlSessionMyBatis框架SQLSession
- Java SSM springmvc spring mybatis 集程式碼生成器 後臺框架原始碼JavaSSMSpringMVCMyBatis框架原始碼
- Java開發工程師最新面試題庫系列——Mybatis框架部分(附答案)Java工程師面試題MyBatis框架
- 【springboot】【java】【MySQL】【mybatis】【db】mybatis初體驗Spring BootJavaMySqlMyBatis
- myBatis原始碼解析-反射篇(4)MyBatis原始碼反射
- SpringBoot第五篇:整合MybatisSpring BootMyBatis