對數據庫的基本操作步驟


對數據庫的基本操作步驟 + 面試題

MyBatis 最初的設計是基於 XML 配置文件的,但隨着 Java 的發展(Java 1.5 開始引入注解)和 MyBatis 自身的迭代升級,終於在 MyBatis 3 之后就開始支持基於注解的開發了。

下面我們使用 Spring Boot + MyBatis 注解的方式,來實現對數據庫的基本操作,具體實現步驟如下。

MyBatis 注解版

1)創建數據表

drop table if exists `t\_user`;
create table `t\_user` (
  `id` bigint(20) not null auto_increment comment '主鍵id',
  `username` varchar(32) default null comment '用戶名',
  `password` varchar(32) default null comment '密碼',
  `nick\_name` varchar(32) default null,
  primary key (`id`)
) engine=innodb auto_increment=1 default charset=utf8;

2)添加依賴

\<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --\>
\<dependency\>
    \<groupId\>org.mybatis.spring.boot\</groupId\>
    \<artifactId\>mybatis-spring-boot-starter\</artifactId\>
    \<version\>2.1.0\</version\>
\</dependency\>
\<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --\>
\<dependency\>
    \<groupId\>mysql\</groupId\>
    \<artifactId\>mysql-connector-java\</artifactId\>
    \<version\>8.0.16\</version\>
\</dependency\>

3)增加配置文件

在 application.yml 文件中添加以下內容:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/learndb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  type-aliases-package: com.interview.model

4)創建實體類

public class UserEntity implements Serializable {
    private static final long serialVersionUID = -5980266333958177105L;
    private Integer id;
    private String userName;
    private String passWord;
    private String nickName;
    public UserEntity(String userName, String passWord, String nickName) {
        this.userName = userName;
        this.passWord = passWord;
        this.nickName = nickName;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassWord() {
        return passWord;
    }
    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
}

5)增加 Mapper 文件

public interface UserMapper {
    @Select("select \* from t\_user")
    @Results({
            @Result(property = "nickName", column = "nick\_name")
    })
    List\<UserEntity\> getAll();

    @Select("select \* from t\_user where id = \#{id}")
    @Results({
            @Result(property = "nickName", column = "nick\_name")
    })
    UserEntity getOne(Long id);

    @Insert("insert into t\_user(username,password,nick\_name) values(\#{userName}, \#{passWord}, \#{nickName})")
    void insert(UserEntity user);

    @Update("update t\_user set username=\#{userName},nick\_name=\#{nickName} where id =\#{id}")
    void update(UserEntity user);

    @Update({"\<script\> ",
            "update t\_user ",
            "\<set\>",
            " \<if test='userName != null'\>userName=\#{userName},\</if\>",
            " \<if test='nickName != null'\>nick\_name=\#{nickName},\</if\>",
            " \</set\> ",
            "where id=\#{id} ",
            "\</script\>"})
    void updateUserEntity(UserEntity user);

    @Delete("delete from t\_user where id =\#{id}")
    void delete(Long id);
}

使用 @Select、@Insert、@Update、@Delete、@Results、@Result 等注解來替代 XML 配置文件。

6)添加 Mapper 包掃描

在啟動類中添加 @MapperScan,設置 Spring Boot 啟動的時候會自動加載包路徑下的 Mapper。

@SpringBootApplication
@MapperScan("com.interview.mapper")
public class MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisApplication.class, args);
    }
}

7)編寫測試代碼

@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisApplicationTests {
    @Autowired
    private UserMapper userMapper;
    @Test
    public void testInsert() {
        userMapper.insert(new UserEntity("laowang", "123456", "老王"));
        Assert.assertEquals(1, userMapper.getAll().size());
    }
}

相關面試題

1.MyBatis 有哪些優缺點?

答:MyBatis 優缺點如下:

優點:

  • 相比於 JDBC 需要編寫的代碼更少
  • 使用靈活,支持動態 SQL
  • 提供映射標簽,支持對象與數據庫的字段關系映射

缺點:

  • SQL 語句依賴於數據庫,數據庫移植性差
  • SQL 語句編寫工作量大,尤其在表、字段比較多的情況下

總體來說,MyBatis 是一個非常不錯的持久層解決方案,它專注於 SQL 本身,非常靈活,適用於需求變化較多的互聯網項目,也是當前國內主流的 ORM 框架。

2.以下不屬於 MyBatis 優點的是?

A:可以靈活的編輯 SQL 語句
B:很好的支持不同數據庫之間的遷移
C:能夠很好的和 Spring 框架集成
D:提供映射標簽支持對象和數據庫的字段映射

答:B

題目解析:因為 MyBatis 需要自己編寫 SQL 語句,但每個數據庫的 SQL 語句有略有差異,所以 MyBatis 不能很好的支持不同數據庫之間的遷移。

3.MyBatis 和 Hibernate 有哪些不同?

答:MyBatis 和 Hibernate 都是非常優秀的 ORM 框架,它們的區別如下:

  • 靈活性:MyBatis 更加靈活,自己可以寫 SQL 語句,使用起來比較方便;
  • 可移植性:MyBatis 有很多自己寫的 SQL,因為每個數據庫的 SQL 可以不相同,所以可移植性比較差;
  • 開發效率:Hibernate 對 SQL 語句做了封裝,讓開發者可以直接使用,因此開發效率更高;
  • 學習和使用門檻:MyBatis 入門比較簡單,使用門檻也更低。

4.“#”和“$”有什么區別?

答:“#”是預編譯處理,“$”是字符替換。 在使用“#”時,MyBatis 會將 SQL 中的參數替換成“?”,配合 PreparedStatement 的 set 方法賦值,這樣可以有效的防止 SQL 注入,保證程序的運行安全。

5.在 MyBatis 中怎么解決實體類屬性名和表字段名不一致的問題?

答:通常的解決方案有以下兩種方式。

① 在 SQL 語句中重命名為實體類的屬性名,可參考以下配置:

\<select id="selectorder" parametertype="int" resultetype="com.interview.order"\>
       select order_id id, order_no orderno form order where order_id=#{id};
\</select\>

② 通過 <resultMap> 映射對應關系,可參考以下配置:

\<resultMap id="BaseResultMap" type="com.interview.mybatislearning.model.UserEntity" \>
    \<id column="id" property="id" jdbcType="BIGINT" /\>
    \<result column="username" property="userName" jdbcType="VARCHAR" /\>
    \<result column="password" property="passWord" jdbcType="VARCHAR" /\>
    \<result column="nick\_name" property="nickName" jdbcType="VARCHAR" /\>
\</resultMap\>
 \<select id="getAll" resultMap="BaseResultMap"\>
    select * from t_user
\</select\>

6.在 MyBatis 中如何實現 like 查詢?

答:可以在 Java 代碼中添加 SQL 通配符來實現 like 查詢,這樣也可以有效的防治 SQL 注入,具體實現如下:

Java 代碼:

String name = "%wang%":
List<User> list = mapper.likeName(name);

Mapper 配置:

\<select id="likeName"\>
    select * form t_user where name like #{name};
\</select\>

7.MyBatis 有幾種分頁方式?

答:MyBatis 的分頁方式有以下兩種:

  • 邏輯分頁,使用 MyBatis 自帶的 RowBounds 進行分頁,它是一次性查詢很多數據,然后在數據中再進行檢索;
  • 物理分頁,自己手寫 SQL 分頁或使用分頁插件 PageHelper,去數據庫查詢指定條數的分頁數據形式。

8.RowBounds 是一次性查詢全部結果嗎?為什么?

答:RowBounds 表面是在“所有”數據中檢索數據,其實並非是一次性查詢出所有數據。因為 MyBatis 是對 JDBC 的封裝,在 JDBC 驅動中有一個 Fetch Size 的配置,它規定了每次最多從數據庫查詢多少條數據,假如你要查詢更多數據,它會在執行 next() 的時候,去查詢更多的數據。 就好比你去自動取款機取 10000 元,但取款機每次最多能取 2500 元,要取 4 次才能把錢取完。對於 JDBC 來說也是一樣,這樣做的好處是可以有效的防止內存溢出。

9.為什么阿里巴巴不允許使用 HashMap 或 Hashtable 作為查詢結果集直接輸出?

答:因為使用 HashMap 或 Hashtable 作為查詢結果集直接輸出,會導致值類型不可控,給調用人員造成困擾,給系統帶來更多不穩定的因素。

10.什么是動態 SQL?

答:動態 SQL 是指可以根據不同的參數信息來動態拼接的不確定的 SQL 叫做動態 SQL,MyBatis 動態 SQL 的主要元素有:if、choose/when/otherwise、trim、where、set、foreach 等。 以 if 標簽的使用為例:

<select id="findUser" parameterType="com.interview.entity.User" resultType="com.interview.entity.User">
      select * from t_user where
      <if test="id!=null">
        id = #{id}
      </if>
      <if test="username!=null">
        and username = #{username}
      </if>
      <if test="password!=null">
        and password = #{password}
      </if>
</select>

11.為什么不建議在程序中濫用事務?

答:因為事務的濫用會影響數據的 QPS(每秒查詢率),另外使用事務的地方還要考慮各方面回滾的方案,如緩存回滾、搜索引擎回滾、消息補償、統計修正等。

12.如何開啟 MyBatis 的延遲加載?

答:只需要在 mybatis-config.xml 設置 <setting name="lazyLoadingEnabled" value="true"/> 即可打開延遲緩存功能,完整配置文件如下:

\<configuration\>
    \<settings\>
        \<!-- 開啟延遲加載 --\>
        \<setting name="lazyLoadingEnabled" value="true"/\>
    \</settings\>
\</configuration\>

13.什么是 MyBatis 的一級緩存和二級緩存?

答:MyBatis 緩存如下:

  • 一級緩存是 SqlSession 級別的,是 MyBatis 自帶的緩存功能,並且無法關閉,因此當有兩個 SqlSession 訪問相同的 SQL 時,一級緩存也不會生效,需要查詢兩次數據庫;
  • 二級緩存是 Mapper 級別的,只要是同一個 Mapper,無論使用多少個 SqlSession 來操作,數據都是共享的,多個不同的 SqlSession 可以共用二級緩存,MyBatis 二級緩存默認是關閉的,需要使用時可手動開啟,二級緩存也可以使用第三方的緩存,比如,使用 Ehcache 作為二級緩存。

手動開啟二級緩存,配置如下:

\<configuration\>
    \<settings\>
        \<!-- 開啟二級緩存 --\>
        \<setting name="cacheEnabled" value="true"/\>
    \</settings\>
\</configuration\>

14.如何設置 Ehcache 為 MyBatis 的二級緩存?

答:可直接在 XML 中配置開啟 EhcacheCache,代碼如下:

\<mapper namespace="com.interview.repository.ClassesReposirory"\> 
    \<!-- 開啟二級緩存 --\>
    \<cache type="org.mybatis.caches.ehcache.EhcacheCache" \>
        \<!-- 緩存創建以后,最后一次訪問緩存的時間至失效的時間間隔 --\>
        \<property name="timeToIdleSeconds" value="3600"/\>
        \<!-- 緩存自創建時間起至失效的時間間隔--\>
        \<property name="timeToLiveSeconds" value="3600"/\>
        \<!-- 緩存回收策略,LRU 移除近期最少使用的對象 --\>
        \<property name="memoryStoreEvictionPolicy" value="LRU"/\>
    \</cache\>

    \<select id="findById" parameterType="java.lang.Long" resultType="com.interview.entity.Classes"\>
        select * from classes where id = #{id}
    \</select\>
\</mapper\>

15.MyBatis 有哪些攔截器?如何實現攔截功能?

答:MyBatis 提供的連接器有以下 4 種。

  • Executor:攔截內部執行器,它負責調用 StatementHandler 操作數據庫,並把結果集通過 ResultSetHandler 進行自動映射,另外它還處理了二級緩存的操作。
  • StatementHandler:攔截 SQL 語法構建的處理,它是 MyBatis 直接和數據庫執行 SQL 腳本的對象,另外它也實現了 MyBatis 的一級緩存。
  • ParameterHandler:攔截參數的處理。
  • ResultSetHandler:攔截結果集的處理。

攔截功能具體實現如下:

@Intercepts({@Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestInterceptor implements Interceptor {
   public Object intercept(Invocation invocation) throws Throwable {
     Object target = invocation.getTarget(); //被代理對象
     Method method = invocation.getMethod(); //代理方法
     Object[] args = invocation.getArgs(); //方法參數
     // 方法攔截前執行代碼塊
     Object result = invocation.proceed();
     // 方法攔截后執行代碼塊
     return result;
   }
   public Object plugin(Object target) {
     return Plugin.wrap(target, this);
   }
}

總結

通過本文可以看出 MyBatis 注解版和 XML 版的主要區別是 Mapper 中的代碼,注解版把之前在 XML 的 SQL 實現,全部都提到 Mapper 中了,這樣就省去了配置 XML 的麻煩。


歡迎關注我的公眾號,回復關鍵字“Java” ,將會有大禮相送!!! 祝各位面試成功!!!

%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.png)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM