Spring
在原始的分層架構實現(xiàn)中,負責響應請求的Controller層依賴于業(yè)務邏輯處理的Service層,而業(yè)務邏輯處理的service層又依賴與數(shù)據(jù)訪問Dao層。上下層間相互依賴耦合,耦合的缺陷在于牽一發(fā)而動全身,不利于后期維護拓展。
為了分層解耦,Spring采用IoC控制反轉和DI依賴注入,來解決耦合問題。
IoC控制反轉
IoC(Inversion of Control)控制反轉的思想:使用對象時,不再主動new產(chǎn)生對象,而是轉換為外部提供對象,此過程中對象創(chuàng)建控制權由程序轉移到外部。
- Spring技術對IoC思想進行了實現(xiàn),Spring提供了一個容器,稱為
IoC
容器,用來充當IoC思想中的“外部”的概念
- IoC容器負責對象的創(chuàng)建、初始化等一系列工作,被創(chuàng)建或者被管理的對象在
IoC
容器中統(tǒng)稱為Bean
DI 依賴注入
IoC控制反轉,相當于將對象交由容器管理,是對象到容器的過程,而DI(Dependency依賴注入)則是從容器中取出對應的對象的過程。
Bean
Bean基礎配置
name
bean標簽指定name屬性配置別名:
scope
bean標簽的scope屬性配置bean的作用范圍,默認scope屬性值為singleton,bean對象是單例的。如果要構建非單例對象,指定屬性值為prototype
Bean實例化方式
bean本質就是一個對象,它的構造方式有以下4種:
- 1、通過無參的構造方法(默認構造方法)實例化bean,因此一個要交給IoC容器的類,如果無參構造方法不存在,想構造bean會拋出異常
BeanCreationException
- 2、使用靜態(tài)工廠實例化bean,先實例化靜態(tài)工廠類,然后執(zhí)行工廠方法屬性字段為靜態(tài)工廠的生產(chǎn)方法。
- 3、使用實例工廠實例化bean。先實例化實例工廠類,然后在實例化bean時指定
factory-method
屬性和factory-bean
屬性。 - 4、使用FactoryBean的方式。這種方式本質上與方式3一樣,只不過spring為我們提供了接口規(guī)范,來編寫bean配置時更加簡潔。
使用工廠方式實例化,便于對實例化過程進行拓展
FactoryBean是Spring提供的接口標準。
Bean的生命周期
對 Prototype Bean 來說,當用戶 getBean 獲得 Prototype Bean 的實例后,IOC 容器就不再對當前實例進行管理,而是把管理權交由用戶,此后再 getBean 生成的是新的實例。
所以我們描述 Bean 的生命周期,都是指的 Singleton Bean。
Bean生命周期過程:
- 第1步,實例化一個Bean對象;construct
- 第2步,為Bean設置相關屬性和依賴;propertiesSet
- 第3步,初始化;init
- 第4步,銷毀;destroy
Bean的生命周期控制方法的設置有兩種方式:
第一種:先在類中提供生命周期控制方法,然后在Bean配置時,在bean標簽中指定init-method 和 destroy - method。
第二種方式:可以通過實現(xiàn)Spring提供的InitializingBean接口和DisposableBean接口:
生命周期的順序:
從上邊可以看到bean標簽上的init方法和destroy方法比實現(xiàn)的接口的方法范圍更大一些(先init,后destroy)。
注意:要想看到destroy的效果,需要我們手動關閉IOC容器。
關閉IOC容器的方式可以是通過容器的close()
方法,也可以是通過容器的registerShutdownHook()
方法。
依賴注入方式
提供set()方法或者使用構造器。
第1種情況:使用set依賴注入一個引用類型。
第2種情況:使用set注入簡單類型
第3種情況,使用構造器注入簡單類型:
第4種情況,使用構造器注入引用類型:
依賴自動裝配
bean標簽使用autowire屬性自動裝配,
autowire屬性值可選擇如下:
Mybatis-Plus
Mybatis-Plus是Mybatis的增強工具,在Mybatis基礎上只做增強,不做改變,為簡化開發(fā),提供效率設計。
配置
添加依賴:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
在Springboot的啟動類中添加@MapperScan注解,掃描mapper包。
@SpringBootApplication
@MapperScan("com.atguigu.mybatisplus.mapper")
public class MybatisplusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisplusApplication.class, args);
}
}
在application.yml中配置日志輸出:
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
BaseMapper
BaseMapper是Mybatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型為操作的實體類型。
以User實體為例:
@Data //lombok注解
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
我們在編寫自己的mapper時,可以繼承這個模板mapper。
public interface UserMapper extends BaseMapper<User> {
}
增:
@Test
public void testInsert(){
User user = new User(null, "張三", 23, "zhangsan@atguigu.com");
//INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
int result = userMapper.insert(user);
System.out.println("受影響行數(shù):"+result);
//1475754982694199298
System.out.println("id自動獲?。?+user.getId());
}
刪:
@Test
public void testDeleteById(){
//通過id刪除用戶信息
//DELETE FROM user WHERE id=?
int result = userMapper.deleteById(1475754982694199298L);
System.out.println("受影響行數(shù):"+result);
@Test
public void testDeleteBatchIds(){
//通過多個id批量刪除
//DELETE FROM user WHERE id IN ( ? , ? , ? )
List<Long> idList = Arrays.asList(1L, 2L, 3L);
int result = userMapper.deleteBatchIds(idList);
System.out.println("受影響行數(shù):"+result);
}
}
@Test
public void testDeleteByMap(){
//根據(jù)map集合中所設置的條件刪除記錄
//DELETE FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 23);
map.put("name", "張三");
int result = userMapper.deleteByMap(map);
System.out.println("受影響行數(shù):"+result);
}
改:
@Test
public void testUpdateById(){
User user = new User(4L, "admin", 22, null);
//UPDATE user SET name=?, age=? WHERE id=?
int result = userMapper.updateById(user);
System.out.println("受影響行數(shù):"+result);
}
查:
@Test
public void testSelectById(){
//根據(jù)id查詢用戶信息
//SELECT id,name,age,email FROM user WHERE id=?
User user = userMapper.selectById(4L);
System.out.println(user);
}
@Test
public void testSelectBatchIds(){
//根據(jù)多個id查詢多個用戶信息
//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
List<Long> idList = Arrays.asList(4L, 5L);
List<User> list = userMapper.selectBatchIds(idList);
list.forEach(System.out::println);
}
@Test
public void testSelectByMap(){
//通過map條件查詢用戶信息
//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 22);
map.put("name", "admin");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
@Test
public void testSelectList(){
//查詢所有用戶信息
//SELECT id,name,age,email FROM user
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
通用Service
說明:
通用 Service CRUD 封裝IService接口,進一步封裝 CRUD 采用 get 查詢單行 remove 刪除 list 查詢集合 page 分頁 前綴命名方式區(qū)分 Mapper 層避免混淆,
泛型 T 為任意實體對象
建議如果存在自定義通用 Service 方法的可能,請創(chuàng)建自己的 IBaseService 繼承
Mybatis-Plus 提供的基類
官網(wǎng)地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%
A3
使用步驟:
1、創(chuàng)建Service接口和實現(xiàn)類。
/**
* UserService繼承IService模板提供的基礎功能
*/
public interface UserService extends IService<User> {
}
/**
* ServiceImpl實現(xiàn)了IService,提供了IService中基礎功能的實現(xiàn)
* 若ServiceImpl無法滿足業(yè)務需求,則可以使用自定的UserService定義方法,并在實現(xiàn)類中實現(xiàn)
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements
UserService {
}
使用案例:
1、測試查詢記錄數(shù):
@Autowired
private UserService userService;
@Test
public void testGetCount(){
long count = userService.count();
System.out.println("總記錄數(shù):" + count);
}
2、測試批量插入:
@Test
public void testSaveBatch(){
// SQL長度有限制,海量數(shù)據(jù)插入單條SQL無法實行,
// 因此MP將批量插入放在了通用Service中實現(xiàn),而不是通用Mapper
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("ybc" + i);
user.setAge(20 + i);
users.add(user);
}
//SQL:INSERT INTO t_user ( username, age ) VALUES ( ?, ? )
userService.saveBatch(users);
}
常用注解
@TableName
在使用MyBatis-Plus實現(xiàn)基本的CRUD時,我們并沒有指定要操作的表,只是在
Mapper接口繼承BaseMapper時,設置了泛型User,而操作的表為user表
由此得出結論,MyBatis-Plus在確定操作的表時,由BaseMapper的泛型決定,即實體類型決
定,且默認操作的表名和實體類型的類名一致
若實體類類型的類名和要操作的表的表名不一致,會出現(xiàn)什么問題?
將表user更名為t_user,測試查詢功能,就會出錯。
此時我們可以使用@TableName注解指定要操作的表名:
還可以使用全局配置:
在開發(fā)的過程中,我們經(jīng)常遇到以上的問題,即實體類所對應的表都有固定的前綴,例如t_或tbl_
此時,可以使用MyBatis-Plus提供的全局配置,為實體類所對應的表名設置默認的前綴,那么就
不需要在每個實體類上通過@TableName標識實體類對應的表
那么可以在application.yml中使用mybatis-plus的配置:
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默認前綴
table-prefix: t_
@TableId
MyBatis-Plus在實現(xiàn)CRUD時,會默認將id作為主鍵列,并在插入數(shù)據(jù)時,默認基于雪花算法的策略生成id。
若實體類和表中表示的主鍵不是id,而是其他字段,如uid,則需要在實體類的主鍵表示的屬性上使用@TableId注解標識主鍵:
@TableId的value屬性
若實體類中主鍵對應的屬性為id,而表中表示主鍵的字段為uid,此時若只在屬性id上添加注解
@TableId,則拋出異常Unknown column ‘id’ in ‘field list’,即MyBatis-Plus仍然會將id作為表的
主鍵操作,而表中表示主鍵的是字段uid
此時需要通過@TableId注解的value屬性,指定表中的主鍵字段,@TableId(“uid”)或
@TableId(value=“uid”)
@TableId的type屬性
type屬性用來定義主鍵策略
配置全局主鍵策略:
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默認前綴
table-prefix: t_
# 配置MyBatis-Plus的主鍵策略
id-type: auto
雪花算法
需要選擇合適的方法去應對數(shù)據(jù)規(guī)模的增長,以應對逐漸增長的訪問壓力和數(shù)據(jù)量。數(shù)據(jù)庫的擴展方式主要包括:業(yè)務分庫、主從復制、數(shù)據(jù)庫分表。
- 垂直分表
垂直分表適合將表中某些不常用且占了大量空間的列拆分出去。
例如,前面示意圖中的 nickname 和 description 字段,假設我們是一個婚戀網(wǎng)站,用戶在篩選其他用
戶的時候,主要是用 age 和 sex 兩個字段進行查詢,而 nickname 和 description 兩個字段主要用于展
示,一般不會在業(yè)務查詢中用到。description 本身又比較長,因此我們可以將這兩個字段獨立到另外
一張表中,這樣在查詢 age 和 sex 時,就能帶來一定的性能提升。 - 水平分表
水平分表適合表行數(shù)特別大的表,有的公司要求單表行數(shù)超過 5000 萬就必須進行分表,這個數(shù)字可以
作為參考,但并不是絕對標準,關鍵還是要看表的訪問性能。對于一些比較復雜的表,可能超過 1000
萬就要分表了;而對于一些簡單的表,即使存儲數(shù)據(jù)超過 1 億行,也可以不分表。
水平分表相比垂直分表,會引入更多的復雜性,例如要求全局唯一的數(shù)據(jù)id該如何處理- 主鍵自增
以最常見的用戶id為例,可以按照1000000 的范圍大小進行分段,1 ~ 999999 放到表 1中,1000000 ~ 1999999 放到表2中,以此類推。這樣的缺點在于,可能出現(xiàn)分布不均勻的情況,有可能某個分段實際存儲的數(shù)據(jù)量只有 1 條,而
另外一個分段實際存儲的數(shù)據(jù)量有 1000 萬條。 - 取模
同樣以用戶 ID 為例,假如我們一開始就規(guī)劃了 10 個數(shù)據(jù)庫表,可以簡單地用 user_id % 10 的值來表示數(shù)據(jù)所屬的數(shù)據(jù)庫表編號,ID 為 985 的用戶放到編號為 5 的子表中,ID 為 10086 的用戶放到編號為 6 的子表中。這種方式的優(yōu)點在于表分布比較均勻,但是缺點在于擴充新的表很麻煩,所有的數(shù)據(jù)都需要重新分布。 - 雪花算法
雪花算法是由Twitter公布的分布式主鍵生成算法,它能夠保證不同表的主鍵的不重復性,以及相同表的有序性。
- 主鍵自增
雪花算法的原理:
雪花算法的優(yōu)點:整體上按照時間自增排序,并且整個分布式系統(tǒng)內不會產(chǎn)生ID碰撞,效率較高。
@TableField
MyBatis-Plus在執(zhí)行SQL語句時,要保證實體類中的屬性名和
表中的字段名一致
如果實體類中的屬性名和字段名不一致的情況,會出現(xiàn)什么問題呢?
情況1:
若實體類中屬性使用駝峰命名風格,而表中字段屬性使用的是下劃線命名風格,例如如實體類屬性userName,表中字段user_name此時MyBatis-Plus會自動將下劃線命名風格轉化為駝峰命名風格相當于在MyBatis中配置。
情況2:
若實體類中的屬性和表中字段不滿足情況1,例如實體類屬性為name,而表中字段為username,此時需要在實體類屬性上使用@TableFile(“username”)設置屬性所對應的字段名。
@TableLogic
- 物理刪除:真實刪除,將對應數(shù)據(jù)從數(shù)據(jù)庫中刪除,之后查詢不到此條被刪除的數(shù)據(jù)
- 邏輯刪除:假刪除,將對應數(shù)據(jù)中代表是否被刪除字段的狀態(tài)修改為“被刪除狀態(tài)”,之后在數(shù)據(jù)庫
中仍舊能看到此條數(shù)據(jù)記錄 - 使用場景:可以進行數(shù)據(jù)恢復
實現(xiàn)邏輯刪除的步驟:
步驟1:數(shù)據(jù)庫中創(chuàng)建邏輯刪除狀態(tài)字段,設置默認值為0
步驟2:實體類中添加邏輯刪除屬性,并添加注解@TableLogic
步驟3:測試
測試刪除功能,真正執(zhí)行的是修改
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
測試查詢功能,被邏輯刪除的數(shù)據(jù)默認不會被查詢
SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
條件構造器和常用接口
Wrapper:條件構造抽象類,最頂端父類。幫助我們構造增刪改查的條件。
QueryWrapper
例1:組裝查詢條件:
@Test
public void test01(){
//查詢用戶名包含a,年齡在20到30之間,并且郵箱不為null的用戶信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
例2:組裝排序條件
UpdateWrapper
插件
MyBatis Plus自帶分頁插件,只要簡單的配置即可實現(xiàn)分頁功能。
使用步驟:
1、添加配置類文章來源:http://www.zghlxwxcb.cn/news/detail-516111.html
@Configuration
@MapperScan("com.atguigu.mybatisplus.mapper") //可以將主類中的注解移到此處
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2、測試文章來源地址http://www.zghlxwxcb.cn/news/detail-516111.html
@Test
public void testPage(){
//設置分頁參數(shù)
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);
//獲取分頁數(shù)據(jù)
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("當前頁:"+page.getCurrent());
System.out.println("每頁顯示的條數(shù):"+page.getSize());
System.out.println("總記錄數(shù):"+page.getTotal());
System.out.println("總頁數(shù):"+page.getPages());
System.out.println("是否有上一頁:"+page.hasPrevious());
System.out.println("是否有下一頁:"+page.hasNext());
}
到了這里,關于Java框架學習(二)SSM體系:Spring、SpringMVC、MybatisPlus的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!