今天寫篇springboot的博客,主要介紹一下springboot搭建以及一些整合。java
首先介紹springboot搭建,我今天選擇Maven,想用Gradle搭建的就本身百度一下吧,訪問「http://start.spring.io/」官網。mysql
填寫好Maven的GroupId以及ArtifactId而後Generate Project。web
我此次使用的是IntellIj IDEA,導入generate出來的project,選擇maven導入,一直選next就好了(記得選一下jdk版本,我用的是1.8),eclipse的話直接import project就好了。spring
初始的項目結構應該就是一個普通的maven項目,只有一個配置文件就是application.properties,也是springboot整合全部東西的配置文件。sql
maven pom文件的依賴只須要如下:數據庫
若是加入web模塊:api
我用的版本是1.5.4數組
能夠按照我上面的項目結構去創建resource文件夾以及package包,注意一點,test和main的根包名須要一致,不然會報錯(具體報錯能夠本身試試)。tomcat
首先來試試最基礎的helloworld。安全
寫一個跟SpringMVC相似的controller就能夠試試helloworld了,在包的根路徑創建一個Application類做爲程序入口(springboot的規矩),也能夠直接運行main方法啓動springboot,至關於內嵌了tomcat。
運行起來以後就能夠在localhost:8080/hello看到映射結果了。
若是使用Test訪問:
Mock一下,而後引入
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
具體代碼不解釋了,看一看能猜出來。
而後來看一下普通restful風格的controller咋寫,通俗點說就是我咋用springboot實現springmvc同樣的東東
package com.zhengyu.web; import com.zhengyu.model.User; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by niezy on 2017/7/26. */ @RestController @RequestMapping(value = "/users") // 經過這裏配置使下面的映射都在/users下 public class UserController { // 建立線程安全的Map static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>()); @RequestMapping(value = "/", method = RequestMethod.GET) public List<User> getUserList() { // 處理"/users/"的GET請求,用來獲取用戶列表 // 還能夠經過@RequestParam從頁面中傳遞參數來進行查詢條件或者翻頁信息的傳遞 List<User> r = new ArrayList<User>(users.values()); return r; } @RequestMapping(value = "/", method = RequestMethod.POST) public String postUser(@ModelAttribute User user) { // 處理"/users/"的POST請求,用來建立User // 除了@ModelAttribute綁定參數以外,還能夠經過@RequestParam從頁面中傳遞參數 users.put(user.getId(), user); return "success"; } @RequestMapping(value = "/{id}", method = RequestMethod.GET) public User getUser(@PathVariable Long id) { // 處理"/users/{id}"的GET請求,用來獲取url中id值的User信息 // url中的id可經過@PathVariable綁定到函數的參數中 return users.get(id); } @RequestMapping(value = "/{id}", method = RequestMethod.PUT) public String putUser(@PathVariable Long id, @ModelAttribute User user) { // 處理"/users/{id}"的PUT請求,用來更新User信息 User u = users.get(id); u.setName(user.getName()); u.setAge(user.getAge()); users.put(id, u); return "success"; } @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public String deleteUser(@PathVariable Long id) { // 處理"/users/{id}"的DELETE請求,用來刪除User users.remove(id); return "success"; } }
同樣的controller如上圖,而後開始測試唄
package com.zhengyu; import com.zhengyu.web.UserController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new UserController()).build(); } @Test public void testUserController() throws Exception { // 測試UserController RequestBuilder request = null; // 一、get查一下user列表,應該爲空 request = MockMvcRequestBuilders.get("/users/"); mvc.perform(request).andExpect(status().isOk()).andExpect(content().string(equalTo("[]"))); // 二、post提交一個user request = post("/users/").param("id", "1").param("name", "測試大師").param("age", "20"); mvc.perform(request).andExpect(content().string(equalTo("success"))); // 三、get獲取user列表,應該有剛纔插入的數據 request = MockMvcRequestBuilders.get("/users/"); mvc.perform(request).andExpect(status().isOk()) .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"測試大師\",\"age\":20}]"))); // 四、put修改id爲1的user request = put("/users/1").param("name", "測試終極大師").param("age", "30"); mvc.perform(request).andExpect(content().string(equalTo("success"))); // 五、get一個id爲1的user request = MockMvcRequestBuilders.get("/users/1"); mvc.perform(request).andExpect(content().string(equalTo("{\"id\":1,\"name\":\"測試終極大師\",\"age\":30}"))); // 六、del刪除id爲1的user request = delete("/users/1"); mvc.perform(request).andExpect(content().string(equalTo("success"))); // 七、get查一下user列表,應該爲空 request = MockMvcRequestBuilders.get("/users/"); mvc.perform(request).andExpect(status().isOk()).andExpect(content().string(equalTo("[]"))); } }
好咯~
下面說一下log,spring1.5.4版本是不支持log4j老用法了,我查了一下而後選擇的是log4j2xml的方式,pom以下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
首先springboot自帶的是logback,咱們首先更改以前的pom,加上
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
除去logback,而後引入log4j2.
而後在resource文件夾底下新建一個log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!--日誌級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration後面的status,這個用於設置log4j2自身內部的信息輸出,能夠不設置,當設置成trace時,你會看到log4j2內部各類詳細輸出--> <!--monitorInterval:Log4j可以自動檢測修改配置 文件和從新配置自己,設置間隔秒數--> <configuration status="WARN" monitorInterval="30"> <!--先定義全部的appender--> <appenders> <!--這個輸出控制檯的配置--> <console name="Console" target="SYSTEM_OUT"> <!--輸出日誌的格式--> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> </console> <!--文件會打印出全部信息,這個log每次運行程序會自動清空,由append屬性決定,這個也挺有用的,適合臨時測試用--> <File name="log" fileName="log/test.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File> <!-- 這個會打印出全部的info及如下級別的信息,每次大小超過size,則這size大小的日誌會自動存入按年份-月份創建的文件夾下面並進行壓縮,做爲存檔--> <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> <!--控制檯只輸出level及以上級別的信息(onMatch),其餘的直接拒絕(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> <!-- DefaultRolloverStrategy屬性如不設置,則默認爲最多同一文件夾下7個文件,這裏設置了20 --> <DefaultRolloverStrategy max="20"/> </RollingFile> <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> </appenders> <!--而後定義logger,只有定義了logger並引入的appender,appender纔會生效--> <loggers> <!--過濾掉spring和mybatis的一些無用的DEBUG信息--> <logger name="org.springframework" level="INFO"></logger> <logger name="org.mybatis" level="INFO"></logger> <root level="all"> <appender-ref ref="Console"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>
而後在application.properties配置文件里加上
logging.config=classpath:log4j2.xml
其實不加也行,起碼我測試這個版本沒問題,隨便你啦,強迫症的加上吧。
下面說說spring jdbcTemplate
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
加上這個依賴,而後咱們開始codeing
建個user類
application.properties加上:
spring.datasource.url=jdbc:mysql://localhost:3306/boot
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
log4j.logger.org.springframework.jdbc.core=DEBUG, file
log4j.logger.org.springframework.jdbc.core.StatementCreatorUtils=TRACE, file
而後建一個UserService,跟springmvc沒什麼兩樣
/** * Created by niezy on 2017/7/26. */ public interface UserService { /** * 新增一個用戶 * * @param name * @param age */ void create(String name, Integer age); /** * 根據name刪除一個用戶高 * * @param name */ void deleteByName(String name); /** * 獲取用戶總量 */ Integer getAllUsers(); /** * 刪除全部用戶 */ void deleteAllUsers(); /** * 根據姓名更新年齡 * @param name * @param age */ void update(String name,int age); /** * 根據姓名查對象 * @param name * @return */ User querySingleUser(String name); }
/** * Created by niezy on 2017/7/26. */ @Service public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; @Override public void create(String name, Integer age) { jdbcTemplate.update("insert into USER(NAME, AGE) values(?, ?)", name, age); } @Override public void deleteByName(String name) { jdbcTemplate.update("delete from USER where NAME = ?", name); } @Override public Integer getAllUsers() { return jdbcTemplate.queryForObject("select count(1) from USER", Integer.class); } @Override public void deleteAllUsers() { jdbcTemplate.update("delete from USER"); } @Override public void update(String name, int age) { jdbcTemplate.update("update user set age = ? where name=? ", age, name); } @Override public User querySingleUser(String name) { User user = new User(); user.setName(name); // 返回對象須要beanPropertyRowMapper映射,查詢條件放到Object數組 return jdbcTemplate.queryForObject("select * from user where name=? ", new Object[] {name}, new BeanPropertyRowMapper<User>(User.class)); } }
而後測試
package com.zhengyu; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.zhengyu.jdbcservice.UserService; /** * Created by niezy on 2017/7/26. */ @RunWith(SpringRunner.class) @SpringBootTest public class JdbcApplicationTests { @Autowired private UserService userSerivce; @Before public void setUp() { // 準備,清空user表 userSerivce.deleteAllUsers(); } @Test public void test() throws Exception { // 插入5個用戶 userSerivce.create("zhangsan", 18); userSerivce.create("lisi", 19); userSerivce.create("wangwu", 20); userSerivce.create("haozi", 25); userSerivce.create("zhengyu", 23); // 查數據庫,應該有5個用戶 Assert.assertEquals(5, userSerivce.getAllUsers().intValue()); // 刪除兩個用戶 userSerivce.deleteByName("zhangsan"); userSerivce.deleteByName("haozi"); userSerivce.update("wangwu", 28); System.out.println(userSerivce.querySingleUser("zhengyu").toString()); // 查數據庫,應該有3個用戶 Assert.assertEquals(3, userSerivce.getAllUsers().intValue()); } }
本身跑一下試試 ~
jdbcTemplate只有一點注意的,返回對象稍微麻煩點,須要按他的BeanPropertyRowMapper規矩來,其實還有不少種玩法,ORM框架都有不少玩法,包括我進攜程之後用的攜程的dal框架,返回Object數組啦,集合啦,List<Map>等等,說到底都是封裝的jdbc,而後有的框架是全mapping,有的是半mapping,包括Hibernate實現的JPA標準,也能夠nativeSql支持數組[]選字段返回,也能夠直接mapping整個類,甚至級聯操做,下次專門寫個博客好好說說ORM
插一點事務的東東,springboot整合jpa jdbctempalte等ORM的depency已經自帶Transaction註解了,也是默認的
你能夠在@Test處加上
@Transactional(propagation = Propagation.REQUIRED)
而後咱們開始spring-data-jpa黑魔法,號稱業務操做幾乎不須要寫任何sql的ORM框架,也是spring進軍ORM的產品
pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
IDEA 1.5.4版本要選擇一下jpa的版本,不然會有引不到JPA註解的bug,我當時就被坑了十幾分鍾,換了不少persistence的depency
老規矩,model開始
package com.zhengyu.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import java.math.BigDecimal; /** * Created by niezy on 2017/7/26. */ @Entity @Table(name = "student") public class Student { @Id @GeneratedValue private Long id; @Column(nullable = false,length = 5) private String name; @Column(nullable = false) private Integer age; @Column(nullable = false) private BigDecimal salary; @Column(nullable = false) private String address; public Student() {} public Student(String name, Integer age) { this.name = name; this.age = age; } public Student(Long id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public Student(String name, Integer age, BigDecimal salary, String address) { this.name = name; this.age = age; this.salary = salary; this.address = address; } public BigDecimal getSalary() { return salary; } public void setSalary(BigDecimal salary) { this.salary = salary; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + ", address='" + address + '\'' + '}'; } }
而後service:
package com.zhengyu.datajpaservice; import com.zhengyu.model.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.math.BigDecimal; import java.util.List; /** * Created by niezy on 2017/7/26. */ public interface StudentRepository extends JpaRepository<Student, Long> { Student findByName(String name); List<Student> findListByName(String name); Student findByNameAndAge(String name, Integer age); @Query("from Student where name=:name") Student findStudent(@Param("name") String name); List<Student> findByNameOrderBySalaryDesc(String name); List<Student> findBySalary(BigDecimal salary); }
測試:
package com.zhengyu; import com.zhengyu.datajpaservice.StudentRepository; import com.zhengyu.model.Student; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.List; /** * Created by niezy on 2017/7/26. */ @RunWith(SpringRunner.class) @SpringBootTest public class SpringdatajpaTest { @Autowired private StudentRepository studentRepository; // @Before // public void setUp() { // // 準備工做 // studentRepository.deleteAll(); // // } @Test @Transactional(propagation = Propagation.REQUIRED) public void test() throws Exception { // 建立10條記錄 studentRepository.save(new Student("AAA", 10, new BigDecimal(20000), "shanghai")); studentRepository.save(new Student("BBB", 20, new BigDecimal(50000), "beijing")); studentRepository.save(new Student("CCC", 30, new BigDecimal(20000), "shanghai")); studentRepository.save(new Student("DDD", 40, new BigDecimal(50000), "beijing")); studentRepository.save(new Student("EEE", 50, new BigDecimal(20000), "shanghai")); studentRepository.save(new Student("EEE", 60, new BigDecimal(50000), "beijing")); studentRepository.save(new Student("EEE", 70, new BigDecimal(20000), "shanghai")); studentRepository.save(new Student("FFF", 60, new BigDecimal(50000), "beijing")); studentRepository.save(new Student("III", 90, new BigDecimal(20000), "shanghai")); studentRepository.save(new Student("JJJ", 100, new BigDecimal(50000), "beijing")); // 測試findAll, 查詢全部記錄 Assert.assertEquals(10, studentRepository.findAll().size()); // 測試findByName, 查詢姓名爲FFF的User Assert.assertEquals(60, studentRepository.findByName("FFF").getAge().longValue()); // 測試findUser, 查詢姓名爲FFF的User Assert.assertEquals(60, studentRepository.findStudent("FFF").getAge().longValue()); // 測試findByNameAndAge, 查詢姓名爲FFF而且年齡爲60的User Assert.assertEquals("FFF", studentRepository.findByNameAndAge("FFF", 60).getName()); // 測試刪除姓名爲AAA的User studentRepository.delete(studentRepository.findByName("AAA")); // 測試findAll, 查詢全部記錄, 驗證上面的刪除是否成功 Assert.assertEquals(9, studentRepository.findAll().size()); // List<Student> stuList = studentRepository.findByNameOrderBySalaryDesc("EEE"); List<Student> stuList = studentRepository.findBySalary(new BigDecimal(20000)); for (Student stu : stuList) { System.out.println(stu.toString()); } List<Student> stuList2 = studentRepository.findListByName("EEE"); for (Student stu : stuList2) { System.out.println(stu.toString()); } } }
application.properties加上:
spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
關於hbm2ddl:
這個屬於hibernate知識點了,我這裏選擇create-drop。
關於spring-data-jpa,確實很輕量很給力
在實際開發過程當中,對數據庫的操做無非就「增刪改查」。就最爲廣泛的單表操做而言,除了表和字段不一樣外,語句都是相似的,開發人員須要寫大量相似而枯燥的語句來完成業務邏輯。
爲了解決這些大量枯燥的數據操做語句,咱們第一個想到的是使用ORM框架,好比:Hibernate。經過整合Hibernate以後,咱們以操做Java實體的方式最終將數據改變映射到數據庫表中。
爲了解決抽象各個Java實體基本的「增刪改查」操做,咱們一般會以泛型的方式封裝一個模板Dao來進行抽象簡化,可是這樣依然不是很方便,咱們須要針對每一個實體編寫一個繼承自泛型模板Dao的接口,再編寫該接口的實現。雖然一些基礎的數據訪問已經能夠獲得很好的複用,可是在代碼結構上針對每一個實體都會有一堆Dao的接口和實現。
因爲模板Dao的實現,使得這些具體實體的Dao層已經變的很是「薄」,有一些具體實體的Dao實現可能徹底就是對模板Dao的簡單代理,而且每每這樣的實現類可能會出如今不少實體上。Spring-data-jpa的出現正可讓這樣一個已經很「薄」的數據訪問層變成只是一層接口的編寫方式。
Spring-data-jpa的能力遠不止本文提到的這些,因爲本文主要以整合介紹爲主,對於Spring-data-jpa的使用只是介紹了常見的使用方式。諸如@Modifying操做、分頁排序、原生SQL支持以及與Spring MVC的結合使用等等內容就不在本文中詳細展開
|