mybatis注解開發:
這幾年來注解開發越來越流行,Mybatis 也可以使用注解開發方式,這樣我們就可以減少編寫 Mapper 映射檔案了。本次我們先圍繞一些基本的 CRUD 來學習,再學習複雜映射關系及延遲加載。
mybatis常用注解說明:
前期準備工作:
使用IDEA軟體制作:
建立Maven工程:
GroupID :是項目組織唯一的辨別符,實際對應JAVA的包的結構,是main目錄裡java的目錄結構。
ArtifactID:是項目的唯一的辨別符,實際對應項目的名稱,就是項目根目錄的名稱。
點選下一步,然後點選完成,maven工程就建好了
并且在pom.xml導入對應的坐标
在resources下建立配置檔案等資訊:
編寫SqlMapperConfig.xml:
<?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">
<!--上面是限制-->
<!--mybatis的主配置檔案-->
<configuration>
<!--導入外部檔案-->
<properties resource="jdbcConfig.properties"></properties>
<!--給實體類取别名-->
<typeAliases>
<package name="com.itmei.domain"/>
</typeAliases>
<!--環境配置-->
<environments default="mysql">
<!--mysql環境-->
<environment id="mysql">
<!--事務選擇-->
<transactionManager type="JDBC"></transactionManager>
<!--資料源-->
<dataSource type="POOLED">
<!--資料庫的基本資訊-->
<property name="driver" value="${jdbc.driver}"/><!--這裡的value要和外置檔案的key一緻-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--注冊接口的别名-->
<mappers>
<package name="com.itmei.dao"/>
</mappers>
</configuration>
編寫jdbcConfig.properties:資料庫的配置資訊
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
使用 Mybatis 注解實作基本 CRUD:
完成增删改查等操作:
建立User實體類
注意:這裡我們故意使實體類的名稱和資料庫列名稱不一樣
使用注解方式開發持久層接口
我們先寫2個查詢方法,查詢所有和查詢一個使用者資訊:
其中裡面的注解
@Select:查詢的語句
@Results:各個屬性的含義,id為目前結果集聲明唯一辨別,value值為結果集映射關系,@Result代表一個字段的映射關系,column指定資料庫字段的名稱,property指定實體類屬性的名稱,jdbcType資料庫字段類型,@Result裡的id值為true表明主鍵,預設false;使用@ResultMap來引用映射結果集,其中value可省略。
@Results相當于xml裡面的< resultMap >标簽
@ResultMap來引用映射結果集(一般使用在查詢封裝)
單個參數
可以接收基本類型,對象類型,集合類型,都可以接收,mybatis可以直接使用這個參數,不用經過任何處理,同時#{}這裡面的名稱可以随便寫。
注解所有的開發:
package com.itmei.dao;
import com.itmei.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserDao {
/**
* 查詢所有
* @return
*/
@Select("select * from user;")
@Results(id = "userMap",
value = {
@Result(id = true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "address",property = "userAddress"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday"),
})
List<User> findAll();
/**
* 通過id查詢一個使用者
* @param id
* @return
*/
@Select("select * from user where id=#{uid}") /*這裡單個參數可以随便填寫*/
@ResultMap("userMap")
User findById(Integer id);
/**
* 儲存使用者
* @param user
* @return
*/
@Insert("insert into user (username,sex,birthday,address) values (#{userName},#{userSex},#{userBirthday},#{userAddress})")
@SelectKey(keyColumn = "id",keyProperty = "userId",before = false,resultType = Integer.class,
statement = {"select last_insert_id()"})/*傳回使用者id的資訊*/
int saveUser(User user);
/**
* 使用者的更新
* @param user
* @return
*/
@Update("update user set username=#{userName},sex=#{userSex},birthday=#{userBirthday},address=#{userAddress} where id=#{userId}")
void updateUser(User user);
/**
* 删除使用者
* @param id
* @return
*/
@Delete("delete from user where id=#{userId}")
int deleteUser(Integer id);
/**
* 查詢總的資訊(聚合函數)
* @return
*/
@Select("select count(*) from user ")
int findTotal();
/**
* 模糊查詢
* @param name
* @return
*/
@Select("select * from user where username like #{userName}")
@ResultMap("userMap")
List<User> findByName(String name);
/*這樣我們就不要編寫UserDao.xml映射檔案*/
}
現在我們編寫測試類:
UserTest測試類:
public class UserTest {
private InputStream in;
private SqlSession session;
private UserDao userDao;
@Before/*在測試執行之前運作*/
public void init() throws Exception{
//1.讀取配置檔案
in= Resources.getResourceAsStream("SqlMapperConfig.xml");
//2.建立工廠對象
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(in);
//3.建立SqlSession對象 并且更改為自動添加事務
session = factory.openSession(true);
//4.通過session對象建立dao的代理對象
userDao = session.getMapper(UserDao.class);
}
@After/*在測試執行之後運作*/
public void end() throws Exception{
//6.釋放資源
session.close();
in.close();
}
}
所有的測試方法和代碼:
package com.itmei.Test;
import com.itmei.dao.UserDao;
import com.itmei.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class UserTest {
private InputStream in;
private SqlSession session;
private UserDao userDao;
@Before/*在測試執行之前運作*/
public void init() throws Exception{
//1.讀取配置檔案
in= Resources.getResourceAsStream("SqlMapperConfig.xml");
//2.建立工廠對象
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(in);
//3.建立SqlSession對象 并且更改為自動添加事務
session = factory.openSession(true);
//4.通過session對象建立dao的代理對象
userDao = session.getMapper(UserDao.class);
}
@After/*在測試執行之後運作*/
public void end() throws Exception{
//6.釋放資源
session.close();
in.close();
}
/**
* 查詢所有使用者
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user:users){
System.out.println(user);
}
}
/**
* 查詢一個使用者
*/
@Test
public void testFindById(){
User user = userDao.findById(41);
System.out.println(user);
//結果:User{userId=41, userName='update user clear cache', userAddress='北京朝陽區', userSex='男', userBirthday=Tue Feb 27 17:47:08 CST 2018}
}
/**
* 儲存使用者
*/
@Test
public void testSaveUser(){
User user=new User();
user.setUserName("MJW");
user.setUserSex("男");
user.setUserAddress("北京");
user.setUserBirthday(new Date());
System.out.println("使用者資訊"+user);
//結果:使用者資訊User{userId=null, userName='MJW', userAddress='北京', userSex='男', userBirthday=Sun Jun 07 15:41:48 CST 2020}
userDao.saveUser(user);
System.out.println("使用者資訊"+user);
//結果:使用者資訊User{userId=69, userName='MJW', userAddress='北京', userSex='男', userBirthday=Sun Jun 07 15:41:48 CST 2020}
}
/**
* 更新使用者,通過id
*/
@Test
public void testupdateUser(){
User user=new User();
user.setUserId(69);
user.setUserName("update");
user.setUserSex("男");
user.setUserAddress("北京");
user.setUserBirthday(new Date());
userDao.updateUser(user);
//調用方法查詢使用者
User u = userDao.findById(69);
System.out.println(u);
//結果:User{userId=69, userName='update', userAddress='北京', userSex='男', userBirthday=Sun Jun 07 15:48:14 CST 2020}
}
/**
* 删除一個使用者
*/
@Test
public void testdelete(){
userDao.deleteUser(68);
}
/**
* 查詢總記錄數
*/
@Test
public void testFindTotal(){
int total = userDao.findTotal();
System.out.println("總記錄數:"+total);
//總記錄數:17
}
/**
* 模糊查詢
*/
@Test
public void testFindByName(){
String name="%王%";
List<User> users = userDao.findByName(name);
for (User user:users){
System.out.println(user);
}
/*User{userId=42, userName='小二王', userAddress='北京金燕龍', userSex='女', userBirthday=Fri Mar 02 15:09:37 CST 2018}
User{userId=43, userName='小二王', userAddress='北京金燕龍', userSex='女', userBirthday=Sun Mar 04 11:34:34 CST 2018}
User{userId=46, userName='老王', userAddress='北京', userSex='男', userBirthday=Wed Mar 07 17:37:26 CST 2018}*/
}
}
使用注解實作複雜關系映射開發
實作複雜關系映射之前我們可以在映射檔案中通過配置< resultMap>來實作,在使用注解開發時我們需要借助@Results 注解,@Result 注解,@One 注解,@Many 注解。
複雜關系映射的注解說明
使用注解實作 (一對一) 複雜關系映射及延遲加載
需求:
加載賬戶資訊時并且加載該賬戶的使用者資訊,根據情況可實作延遲加載。(注解方式實作)
添加 User 實體類及 Account 實體類
User 實體類
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
/**
* @author 黑馬程式員
* @Company http://www.ithiema.com
*/
public class User implements Serializable{
private Integer userId;
private String userName;
private String userAddress;
private String userSex;
private Date userBirthday;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userAddress='" + userAddress + '\'' +
", userSex='" + userSex + '\'' +
", userBirthday=" + userBirthday +
'}';
}
}
Account 實體類 :實體類屬性名稱和資料庫列一緻
package com.itheima.domain;
import java.io.Serializable;
/**
* @author 黑馬程式員
* @Company http://www.ithiema.com
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//多對一(mybatis中稱之為一對一)的映射:一個賬戶隻能屬于一個使用者
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
添加User對象引用
添加Account賬戶的持久層接口并使用注解配置
@One:相當于< association>的配置(一對一)
select屬性:代表要執行的sql語句(這裡全限定類名加方法名稱這樣他就可以知道對應sql語句)
fetchType屬性:代表加載方式,一般如果要延遲加載都設定為 LAZY 的值
添加UserDao使用者的持久層接口并使用注解配置
測試一對一關聯及延遲加載
AccountTset測試類:
package com.itmei.Test;
import com.itmei.dao.AccountDao;
import com.itmei.dao.UserDao;
import com.itmei.domain.Account;
import com.itmei.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class AccountTest {
private InputStream in;
private SqlSession session;
private AccountDao accountDao;
@Before/*在測試執行之前運作*/
public void init() throws Exception{
//1.讀取配置檔案
in= Resources.getResourceAsStream("SqlMapperConfig.xml");
//2.建立工廠對象
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(in);
//3.建立SqlSession對象 并且更改為自動添加事務
session = factory.openSession(true);
//4.通過session對象建立dao的代理對象
accountDao = session.getMapper(AccountDao.class);
}
@After/*在測試執行之後運作*/
public void end() throws Exception{
//6.釋放資源
session.close();
in.close();
}
/**
* 查詢所有使用者
*/
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
/* for (Account account:accounts){
System.out.println(account);
}*/
}
}
運作後發現什麼都沒有顯示,原因是我沒有輸出,我如果把for循環不注釋,你就看不出延遲加載的差別,我們現在在pom.xml裡面多寫一個坐标,并且建立一個log4j.properties的資料資訊,我會把代碼放進去。
添加完成後,在運作可以看出代碼的日志資訊,我們隻通路了account的表。
按正常道理當調用了findAll方法就會執行注解的所有資料,但是通過fetchType=FetchType.LAZY 實作延遲加載(按需加載),在進行表的關聯查詢時,按照設定延遲規則推遲對關聯對象的select查詢。例如在進行一對多查詢的時候,隻查詢出一方,當程式中需要多方的資料時,mybatis再發出sql語句進行查詢,這樣子延遲加載就可以的減少資料庫壓力。
我們for循環周遊需要查詢user表和account表時我們可以看出,查詢了user和account表
FetchType 有三個參數分别是延遲加載,預設加載,立即加載
log4j.properties的代碼:
建立一個log4j.properties把下面代碼直接複制到裡面
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
使用注解實作 (多對一) 複雜關系映射
需求:
查詢使用者資訊時,也要查詢他的賬戶清單。使用注解方式實作。
分析:
一個使用者具有多個賬戶資訊,是以形成了使用者(User)與賬戶(Account)之間的一對多關系。
3.3.3.1 User 實體類加入 List< Account>
3.3.3.2 編寫UserDao使用者的持久層接口并使用注解配置
3.3.3.3 編寫AccountDao賬戶的持久層接口并使用注解配置
UserTest添加測試方法
package com.itmei.Test;
import com.itmei.dao.AccountDao;
import com.itmei.dao.UserDao;
import com.itmei.domain.Account;
import com.itmei.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream in;
private SqlSession session;
private UserDao userDao;
@Before/*在測試執行之前運作*/
public void init() throws Exception{
//1.讀取配置檔案
in= Resources.getResourceAsStream("SqlMapperConfig.xml");
//2.建立工廠對象
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(in);
//3.建立SqlSession對象 并且更改為自動添加事務
session = factory.openSession(true);
//4.通過session對象建立dao的代理對象
userDao = session.getMapper(UserDao.class);
}
@After/*在測試執行之後運作*/
public void end() throws Exception{
//6.釋放資源
session.close();
in.close();
}
/**
* 查詢所有使用者
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
/* for (User user:users){
System.out.println("----每個賬戶的資訊-----");
System.out.println(user);
}*/
}
}
運作結果:因為使用延遲加載,是以我們沒有調用裡面的方法他隻會查詢user表,使for循環那麼會調用方法查詢使用者的資訊,簡稱按需加載。
調用方法查詢user表和account表(按需加載)
mybatis 基于注解的二級緩存
在 SqlMapConfig 中開啟二級緩存支援
在持久層接口中使用注解配置二級緩存
在要配置二級緩存的持久層接口上配置
總結:
mybatis的入門
mybatis的環境搭建
第一步:建立maven工程并導入坐标
第二步:建立實體類和dao的接口
第三步:建立Mybatis的主配置檔案
SqlMapConifg.xml
第四步:建立映射配置檔案
IUserDao.xml
環境搭建的注意事項:
第一個:建立IUserDao.xml 和 IUserDao.java時名稱是為了和我們之前的知識保持一緻。
在Mybatis中它把持久層的操作接口名稱和映射檔案也叫做:Mapper
是以:IUserDao 和 IUserMapper是一樣的
第二個:在idea中建立目錄的時候,它和包是不一樣的
包在建立時:com.itheima.dao它是三級結構
目錄在建立時:com.itheima.dao是一級目錄
第三個:mybatis的映射配置檔案位置必須和dao接口的包結構相同
第四個:映射配置檔案的mapper标簽namespace屬性的取值必須是dao接口的全限定類名
第五個:映射配置檔案的操作配置(select),id屬性的取值必須是dao接口的方法名
當我們遵從了第三,四,五點之後,我們在開發中就無須再寫dao的實作類。
mybatis的入門案例
第一步:讀取配置檔案
第二步:建立SqlSessionFactory工廠
第三步:建立SqlSession
第四步:建立Dao接口的代理對象
第五步:執行dao中的方法
第六步:釋放資源
注意事項:
不要忘記在映射配置中告知mybatis要封裝到哪個實體類中
配置的方式:指定實體類的全限定類名
基于注解的入門案例:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL語句
同時需要在SqlMapConfig.xml中的mapper配置時,使用class屬性指定dao接口的全限定類名。
明确:
我們在實際開發中,都是越簡便越好,是以都是采用不寫dao實作類的方式。
不管使用XML還是注解配置。