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
- 汪雲飛. 《spring boot實戰》
- http://www.cnblogs.com/ityouknow/p/6037431.html
- https://github.com/mybatis/spring-boot-starter/wiki/Quick-Start
- https://www.bysocket.com/?p=1610