Spring整合MyBatis框架
mybatis
- 動態sql
<foreach collection="要遍歷的集合" open="起始符號" close="結束符號" separator="分隔符" item="臨時變數名">
<if test="條件">sql片段</if>
<where> 去掉多餘and
<set> 去掉多餘的,
-
分頁 2.1) 物理分頁 使用sql語句進行分頁(mysql limit), 分頁sql不通用, 效率高 2.2) 邏輯分頁 全部查詢,sql簡單通用, 效率低,只適用於資料量少的情況 sqlSession.selectList(“sql id名”, new RowBounds(下標, 大小));
-
資料庫與實體類不一致的情況 first_name firstName 方法1: 在select語句中使用列別名, 只要讓別名與屬性對應 方法2:
<select resultMap="id值" ...>
<resultMap id="值">
<id column="主鍵列名" property="屬性名">
<result column="列名" property="屬性名">
</resultMap>
- 連線查詢的對映(複雜的結果對映)
select … from product p inner join category c on p.category_id=
class Product { id name… private Category category; // id,name… 關係屬性 }
class Category {
}
<resultMap id="值" type="Product">
<id column="主鍵列名" property="屬性名">
<result column="列名" property="屬性名">
<assoiation property="category" javaType="Category">
<id>
<result >
</assoiation>
</resultMap>
-
插入資料時 可以用來把自增列生成的主鍵值賦值給實體類的屬性
-
#{} ${}
#{}
生成? 佔位符,${}
是直接拼接字串,有注入攻擊風險#{}
不能運算,${}
可以運算#{}
只能替換值,不能替換(表名,列名,關鍵字), 而${}
都可以
==========================================================================
spring框架
用途
- 整合其他框架,讓他們協同工作
- 提供了宣告式的事務管理 – 通過配置(xml, 註解)來管理事務
兩大核心思想:IOC,AOP
1. IOC (控制反轉)
class servlet extends HttpServlet{ init() service() destroy() }
建立不是程式設計師來管的 --> tomcat容器 類中的某些方法也不需要我們自己呼叫 --> 由tomcat來呼叫
控制反轉(Inversion of controll):把物件的建立以及某些方法的呼叫交由容器來管理 spring容器,類似於tomcat容器,但它能容納的物件型別更為豐富:dao,service,controller
class UserDao { … }
UserDao dao = new UserDao(); // 原來我們需要自己建立物件 UserDao dao; // spring會建立好
2. 使用步驟
- 在pom.xml檔案中加入spring依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
- 編寫spring的配置檔案, 並定義要控制反轉的類
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--受spring容器管理類, 使用<bean>標籤管理
id="唯一標識"
-->
<bean id="userDao" class="com.westos.dao.UserDao">
</bean>
</beans>
- 使用spring容器
// 1. 建立spring容器
// 根據xml檔案應用程式Context容器(上下文)
// classpath指配置檔案的位置, 起點有java, resources. 寫路徑時相對這個起點去寫
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("abc/spring.xml");
// 2. 使用容器內的物件
// UserDao dao = (UserDao)context.getBean("userDao"); // 根據id獲取
UserDao dao = context.getBean(UserDao.class);// 根據型別獲取, 找一個型別為UserDao的物件
dao.insert();
注意:
- bean id是嚴格區分大小寫的 如果id不存在會 NoSuchBeanDefinitionException
- 如果容器中同一個型別的bean有多個,再根據型別獲取的話 NoUniqueBeanDefinitionException
- bean一般需要無參構造,spring會呼叫它來建立物件 如果沒有無參構造,需要給構造方法的引數賦值:
<constructor-arg index="引數下標" value="值"/>
- 控制物件個數 預設情況下,每個型別的bean在容器中只有一個物件(單例)
多例,每用一次,建立一個新的物件
如果配置多例<bean scope="prototype">
-
初始化以及銷燬方法
<bean init-method="初始化方法名" destroy-method="銷燬方法名">
單例物件既會呼叫初始化,也會呼叫銷燬方法 多例物件,每使用一個多例物件,會呼叫初始化方法,但所有多例物件都不會呼叫銷燬方法 -
依賴注入(看做控制反轉的一個補充) tomcat和spring容器都實現了控制反轉, 但spring容器有依賴注入的功能,而tomcat沒有 DI (dependency injection)
給bean的屬性賦值的過程,稱為依賴注入,是由spring來管理bean與bean之間的依賴關係
<property name="要注入的屬性名" ref="要依賴的bean 的id值"/>
- 依賴注入的三種方式 方式1: set注入 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; }
<property name="要注入的屬性名" ref="要依賴的bean 的id值"/>
方法2: 構造注入 public UserService(UserDao userDao) { this.userDao = userDao; }
<constructor-arg index="0" ref="userDao"/>
方法3: 註解注入
@Autowired
(spring提供的) 利用這個註解完成屬性的賦值,把它加在需要注入的屬性上, 或set方法上,或構造方法上
要啟用該註解
<context:annotation-config/>
@Resource
(java官方的註解) 用法與@Autowired類似
- 可以支援將*.properties 注入到spring.xml中
<!--讀取properties 檔案 location="檔案位置" placeholder 佔位符-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 可以利用 ${key} 獲取 *.properties 檔案中的值-->
<bean id="productDao" class="com.westos.dao.ProductDao">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
註解方式 @Value 可以完成這種值注入 @Value("${properties中的key}")
要保證spring.xml的配置檔案不要修改,要修改的內容可以配置到*.properties檔案中
- 整合mybatis 步驟1:在pom.xml加入依賴 mybatis, mysql, spring-context, logback, druid, junit, spring-jdbc, mybatis-spring
步驟2:把關鍵物件spring控制反轉 連線池物件, sqlSessionFactory, sqlSession
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1) 把資料來源物件交給spring容器管理 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.max}"/>
<property name="minIdle" value="${jdbc.min}"/>
</bean>
<!-- 2) sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入連線池 -->
<property name="dataSource" ref="aaa"/>
<!-- 注入mapper.xml檔案的位置-->
<property name="mapperLocations" value="classpath:com/westos/mapper/*.xml"/>
</bean>
<!-- 3) sqlSession, 用SqlSessionTemplate得到的SqlSession可以不用我們自己操心事務的管理,以及關閉操作 -->
<bean id="sql" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
步驟3: 使用SqlSession工廠
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("abc/spring-mybatis.xml");
SqlSession sqlSession = context.getBean(SqlSession.class);
Map<String, Object> map = new HashMap<String,Object>();
map.put("m", "0");
map.put("n", 5);
List<Product> list = sqlSession.selectList("com.westos.mapper.ProductMapper.selectByPage", map);
for (Product product : list) {
System.out.println(product);
}
1. 通過介面mapper來管理sql語句
spring.xml配置檔案
<!-- 3) sqlSession工廠 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 4) 搜尋有哪些mapper介面, 把每一個mapper介面配置成spring中的一個bean -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- (起始)包名, 從這個包開始掃描-->
<property name="basePackage" value="com.westos.mapper"/>
</bean>
介面:
public interface ProductMapper {
// 實現類由mybatis自動生成(動態代理)
@Insert("insert into product(name,price) values(#{name}, #{price}) ")
void insert(Product product);
@Update("update product set name=#{name}, price=#{price} where id=#{id}")
void update(Product product);
@Delete("delete from product where id=#{id}")
void delete(int id);
@Select("select * from product where id=#{id}")
Product findById(int id);
@Select("select * from product limit #{m}, #{n}")
List<Product> findByPage(Map<String,Object> map);
@Select("select * from product")
List<Product> findByPage2(RowBounds rowBounds);
@Select("select count(*) from product")
int findCount();
}
介面方式的mapper,比較適合寫基本和簡單的sql,如果遇到複雜的sql(例如動態sql,表連線對映等),可以藉助原來的xml mapper
介面mapper和xml mapper結合:
- 目錄結構要一致
- xml中 namespace的取值與介面的包名類名要一致
- 介面中的方法名與xml中標籤id對應
- 介面中的方法名不能重複(不支援過載) 會報錯誤:java.lang.IllegalArgumentException: Mapped Statements collection already contains value for xxxx
mybatis的mapper對映中,預設方法最多隻接收一個引數, 多個引數需要用map或list等集合包裝
要突破這個限制需要使用@Param註解,把它加在方法引數上,用它建立方法引數和sql中#{}之間的聯絡:
@Select("select * from product limit #{m}, #{n}")
// *.java -> *.class 方法的引數名資訊會丟失,所以再按m名稱去找無法找到
List<Product> findByPage3(@Param("m") int x, @Param("n") int y);
2. maven的一些常用設定
- 修改專案的預設jdk版本(預設是1.5 ) 加在pom.xml的最後
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
- 對整個專案字元編碼的設定 寫在dependencies之前
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
3. spring 中的宣告式事務管理
transaction -> tx 步驟1:啟用事務
註解驅動的方式來管理事務
<tx:annotation-driven/>
步驟2:配置事務管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
步驟3:使用 @Transactional 它可以加在方法上或者類上 加在方法上,表示此方法受事務管理 加在類上,那麼這個類中的所有方法都受事務管理
3.1 @Transactional註解詳解
預設情況下,只有方法出現的 是RuntimeException或Error以及它們的子類時(未檢查異常),才會導致事務回滾
如果要改變預設情況 @Transactional(rollbackFor=異常類.class) 那麼方法如果出現了該異常,或該異常的子類異常時,就會回滾
@Transactional(noRollbackFor=異常類.class) 當遇到這種異常時,不會回滾事務
最後要注意的是,在業務方法中不要自己try-catch捕獲異常,否則spring無法自動回滾事務
@Transactional(isolation = 隔離級別)
@Transactional(timeout = 超時時間(秒))
@Transactional(readOnly = true|false) true表示只讀(只有查詢) false(會有增刪改) 設定為true,效能會有所提升,但是底層資料庫驅動支援(對mysql支援)
@Transactional(propagation = 傳播行為) REQUIRED (預設值) required 必須的 (如果沒有事務,開始新的;如果有了用已有的) SUPPORTS 支援的 (如果沒有事務,不開始新的;如果有了用已有的) REQUIRES_NEW 需要新的 (總會開始新事務)
只有兩個業務類的方法相互呼叫時,傳播行為才會有效
ProductService 商品業務類
@Transactional(propagation=REQUIRED)
biz1() { // 事務1
biz2()
}
OrderService 訂單業務類
@Transactional(propagation=REQUIRES_NEW)
biz2(); // 事務2