1. 程式人生 > >Spring Boot 的數據訪問:JPA 和 MyBatis

Spring Boot 的數據訪問:JPA 和 MyBatis

raw -m manager varchar 127.0.0.1 bind temp builder tro

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

Spring Boot 的數據訪問:JPA 和 MyBatis