Spring Boot 的數據訪問:JPA 和 MyBatis

JPA(Java Persistence API)是一個基於O/R映射(Object-Relational Mapping)的標準規範,主要實現包括Hibernate、EclipseLink和OpenJPA等。

orm框架的本質是簡化編程中操作數據庫的編碼[2],JPA 方便程序員不寫sql語句,而 MyBatis 呢,則適合靈活調試動態sql。
本文梳理了springboot整合jpa和mybatis的大體過程,並給出了兩個demo。

1 在docker環境下運行數據庫

首先安裝vmware虛擬機,然後安裝docker。
在docker中 pull Oracle鏡像並啓動容器。
最後把虛擬機的1521端口映射到宿主機上。

  • 數據庫:Oracle XE 11g
  • 宿主機數據庫客戶端:SQL developer

注意必須先禁用/etc/selinux/config中selinux選項,然後重啓系統。否則安裝好的oracle沒有默認實例  
[root@localhost fsj]# docker pull wnameless/oracle-xe-11g

查看已經安裝的鏡像
[root@localhost fsj]# docker images 

啓動容器
[root@localhost fsj]# docker run -d -p 9091:8080 -p 1521:1521 --name xe wnameless/oracle-xe-11g
9bf0a03006471a2268b239c32bed00737ee94ef93f92650226c056b0fb891b40
[root@localhost fsj]# netstat -nlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      948/sshd            
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1051/master         
tcp6       0      0 :::1521                 :::*                    LISTEN      5403/docker-proxy-c 
tcp6       0      0 :::22                   :::*                    LISTEN      948/sshd            
tcp6       0      0 ::1:25                  :::*                    LISTEN      1051/master         
tcp6       0      0 :::9091                 :::*                    LISTEN      5396/docker-proxy-c 
udp        0      0 0.0.0.0:68              0.0.0.0:*                           767/dhclient        
udp        0      0 0.0.0.0:47439           0.0.0.0:*                           767/dhclient        
udp6       0      0 :::17787                :::*                                767/dhclient        
raw6       0      0 :::58                   :::*                    7           646/NetworkManager


[root@localhost fsj]# docker ps -a
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                      PORTS                                                    NAMES
2865df7bf94a        wnameless/oracle-xe-11g   "/bin/sh -c '/usr/sbi"   5 seconds ago       Up 2 seconds                22/tcp, 0.0.0.0:1521->1521/tcp, 0.0.0.0:9091->8080/tcp   xe

進入容器的shell: docker exec -it container-id/container-name bash
[root@localhost fsj]# docker exec -it xe bash 

然後以system/oracle登陸。
$ sqlplus system/oracle

表空間

創建
SQL> create tablespace ts_fsj
    datafile '/u01/app/oracle/oradata/XE/ts_fsj.dbf'
    size 50m
    autoextend on
    maxsize 10g;

查看錶空間
SQL>  select name from v$datafile
SQL> select file_name,tablespace_name from dba_data_files;

新建用戶

create user boot
  identified by boot
  default tablespace ts_fsj
  temporary tablespace temp
  profile default

grant connect,resource,dba to boot

2 springboot整合jpa

新建maven項目:

mvn archetype:generate \
  -DgroupId=com.fsj \
  -DartifactId=ex01 \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

具體代碼

要建立數據訪問DAO層,需要定義一個繼承了JpaRepository的接口。然後編寫查詢方法,例如

public interface PersonRepository extends JpaRepository<Person, Long> {
    // 通過Address相等查詢,參數爲address
    List<Person> findByAddress(String address);

    Person findByNameAndAddress(String name, String address);

    @Query("select p from Person p where p.name= :name and p.address= :address")
    Person withNameAndAddressQuery(@Param("name") String name, @Param("address") String address);

    // 對應Person實體中的 @NameQuery
    Person withNameAndAddressNamedQuery(String name, String address);
}

簡單的查詢方法包括:

1、通過屬性名查詢

使用了findBy、Like、And等關鍵字命名方法,那麼就不需要編寫具體的sql了,比如

通過Address相等查詢,參數爲address,可以寫成List<Person> findByAddress(String address);

相當於JPQL: select p from Person p where p.address=?1

完整的查詢關鍵字見:Spring Data JPA 查詢方法支持的關鍵字

2、使用@Query註解

3、使用@NameQuery註解

支持用JPA的NameQuery來定義查詢方法,一個名稱映射一個查詢語句

dao層寫好之後就可以在controller層直接調用了。

3 springboot整合mybatis

具體代碼

1、新建maven項目

2、修改pom文件,添加依賴包

3、修改application.properties 添加相關配置

4、在數據庫中添加測試用city表和數據

CREATE TABLE city (
    id VARCHAR2(32) NOT NULL ,
    name VARCHAR2(64),
    state VARCHAR2(64),
    country VARCHAR2(64),
    PRIMARY KEY(ID)
);

INSERT INTO city (id, name, state, country) VALUES ('1', 'San Francisco', 'CA', 'US');
INSERT INTO city (id, name, state, country) VALUES ('2', 'Beijing', 'BJ', 'CN');
INSERT INTO city (id, name, state, country) VALUES ('3', 'Guangzhou', 'GD', 'CN');
COMMIT ;

5、開發Mapper。使用註解和xml兩種方式

public interface CityMapper {

    @Select("SELECT id, name, state, country FROM city WHERE state = #{state}")
    City queryByState(String state);

    @Select("select * from city")
    List<City> queryAll();

    //xml方式,適合複雜查詢
    List<City> fuzzyQuery(@Param("name") String name);
}

對應的CityMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.fsj.dao.CityMapper">
    <sql id="Base_Column_List">
        id, name, state, country
    </sql>
    <resultMap id="BaseResultMap" type="com.fsj.entity.City">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="state" property="state"/>
        <result column="country" property="country"/>
    </resultMap>

    <select id="fuzzyQuery" resultMap="BaseResultMap">
        SELECT * FROM CITY
        WHERE 1>0
        <if test="name != null and name != '' ">
            <bind name="fuzzyname" value=" '%'+name+'%' "/>
            AND UPPER(name) like #{fuzzyname, jdbcType=VARCHAR}
        </if>
    </select>
</mapper>

6、添加測試

@RunWith(SpringRunner.class)
@SpringBootTest
//@Transactional
public class CityTest {
    private MockMvc mvc1;
    private MockMvc mvc2;
    private String url_get_all;

    @Autowired
    CityMapper cityMapper;

    @Autowired
    WebApplicationContext webApplicationContext;

    @Before
    public void setUp() throws Exception {
        mvc1 = MockMvcBuilders.standaloneSetup(new CityController()).build();
        mvc2 = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        url_get_all = "/mybatis/queryAll";
    }

    @Test
    public void testQueryAll() throws Exception {
        List<City> expected = cityMapper.queryAll();
        List<City> actual = cityMapper.queryAll();
        Assert.assertEquals("queryAll測試失敗", JSON.toJSONString(expected), JSON.toJSONString(actual));
    }

    /**
     * 驗證controller是否正常響應並打印返回結果
     * http://www.ityouknow.com/springboot/2017/05/09/springboot-deploy.html
     * 此處MockMvc對象請求失敗,可能只適用於springboot1.3.6舊版本
     * @throws Exception
     */
    @Test
    public void testRequest() throws Exception {
        mvc1.perform(MockMvcRequestBuilders.get(url_get_all).accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

    /*output
    * org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
*/
    }

    @Test
    public void testRequest2() throws Exception {
        MvcResult res = mvc2.perform(MockMvcRequestBuilders.get(url_get_all).accept(MediaType.APPLICATION_JSON))
                .andReturn();
        int status = res.getResponse().getStatus();
        String content = res.getResponse().getContentAsString();
        List<City> expected = cityMapper.queryAll();

        Assert.assertEquals(200, status);
        Assert.assertEquals(JSON.toJSONString(expected), content);//json元素順序不同,測試不過

        /*json對象比較,在python中自動排序,對象比較爲True
         Expected :[{"country":"US","id":"1","name":"San Francisco","state":"CA"},{"country":"CN","id":"2","name":"Beijing","state":"BJ"},{"country":"CN","id":"3","name":"Guangzhou","state":"GD"}]
         Actual   :[{"id":"1","name":"San Francisco","state":"CA","country":"US"},{"id":"2","name":"Beijing","state":"BJ","country":"CN"},{"id":"3","name":"Guangzhou","state":"GD","country":"CN"}]
         * */
    }
}

References

  1. 汪雲飛. 《spring boot實戰》
  2. http://www.cnblogs.com/ityouknow/p/6037431.html
  3. https://github.com/mybatis/spring-boot-starter/wiki/Quick-Start
  4. https://www.bysocket.com/?p=1610
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章