[TOC] SpringBoot基礎(二) 一、操作資料庫 1. SpringBootJdbc 1. 引入依賴 jdbc 和 mysql 2. SpringBoot預設支持的連接池策略,如果使用 jdbc 或者 jpa 就會自動連接連接池 優先尋找創建Tomcat連接池 如果沒有Tomcat連接池, ...
目錄
SpringBoot基礎(二)
一、操作資料庫
1. SpringBootJdbc
引入依賴 jdbc 和 mysql
SpringBoot預設支持的連接池策略,如果使用 jdbc 或者 jpa 就會自動連接連接池
- 優先尋找創建Tomcat連接池
- 如果沒有Tomcat連接池,會查找創建HikariCP
- 如果沒有HikariCP連接池,會查找創建dbcp
- 如果沒有dbcp連接池,會查找創建dbcp2
- 可以使用spring.datasource.type屬性指定連接池類型,比如其它的連接池 C3P0 或者 druid
application-jdbc.yml
#資料庫jdbc連接url地址,serverTimezone設置資料庫時區東八區 spring: datasource: url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8 username: xxx password: xxxxx driver-class-name: com.mysql.jdbc.Driver
UserDao 介面
public interface UserService { //獲取全部用戶數據 public List<User> getUserList(); //新增用戶數據 public void createUser(User user); //獲取指定id用戶信息 public User getUser(Long id); //更新指定id用戶信息 public void updateUser(Long id,User user); //刪除指定id用戶 public void deleteUser(Long id); }
UserDaoImpl
@Service public class UserServiceImpl implements UserService { //SpringBoot提供的資料庫操作類 @Autowired JdbcTemplate jdbcTemplate; @Override public List<User> getUserList() { return jdbcTemplate.query("select * from users", new BeanPropertyRowMapper(User.class)); } @Override public void createUser(User user) { jdbcTemplate.update("insert into users(name,age)values(?,?)",user.getName(),user.getAge()); } @Override public User getUser(Long id) { final User user = new User(); /* jdbcTemplate.query("select * from users where id="+id,new RowCallbackHandler() { * @Override * public void processRow(ResultSet rs) throws SQLException { * user.setId(id); * user.setName(rs.getString("name")); * user.setAge(rs.getInt("age")); * } * }); */ return jdbcTemplate.queryForObject("select * from users where id=?",new BeanPropertyRowMapper(User.class),id); return user; } @Override public void updateUser(Long id, User user) { jdbcTemplate.update("update users set name=?,age=? where id=?",user.getName(),user.getAge(),id); } @Override public void deleteUser(Long id) { jdbcTemplate.update("delete from users where id=?",id); } }
2. SpringBoot 整合 Mybatis
引入依賴。mybatis,mysql,druid。
配置 application.yml
#資料庫jdbc連接url地址,serverTimezone設置資料庫時區東八區 spring: datasource: url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8 username: xxx password: xxxxx driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #springboot整合mybatis mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.demo.domain
創建 實體bean、dao介面以及相應的 mapper 映射文件
3. SpringBott 使用JPA
1. JPA 介紹
- (Java Persistence(持久化) API):是註解或XML描述對象-關係表的映射關係,並將運行期的實體對象持久化到資料庫中。
2. 優勢
- 標準化:JPA 是 JCP 組織發佈的 Java EE 標準之一,因此符合 JPA 標準的框架都提供相同的訪問API,這保證了基於JPA開發的企業應用能夠經過少量的修改就能夠在不同的JPA框架下運行。JPA的實現產品有HIbernate,TopLink,OpenJPA等等。
- 容器級特性的支持:JPA框架中支持大數據集、事務、併發等容器級事務。
- JPA簡單易用,集成方便:JPA的主要目標之一就是提供更加簡單的編程模型。只需要使用 javax.persistence.Entity進行註釋。JPA基於非侵入式原則設計,因此可以很容易的和其它框架或者容器集成。
- 查詢能力:JPA的查詢語言(JPQL)是面向對象而非面向資料庫的。而且能夠支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能夠提供的高級查詢特性,甚至還能夠支持子查詢。
- 高級特性:JPA 中能夠支持面向對象的高級特性,如類之間的繼承、多態和類之間的複雜關係,這樣的支持能夠讓開發者最大限度的使用面向對象的模型設計企業應用,而不需要自行處理這些特性在關係資料庫的持久化。
3. JPA 註解
註解 | 作用 |
---|---|
@Entity | 聲明類為實體或表 |
@Table | 聲明表名 |
@Basic | 指定非約束明確的各個欄位 |
@Embedded | 指定類或它的值是一個可嵌入的類的實例的實體的屬性 |
@Id | 指定的類的屬性,用於識別(一個表中的主鍵) |
@GeneratedValue | 指定如何標識屬性可以被初始化,例如自動、手動、或從序列表中獲得的值 |
@Transient | 指定的屬性,它是不持久的,即:該值永遠不會存儲在資料庫中 |
@Column | 指定持久屬性欄屬性 |
@SequenceGenerator | 指定在@GeneratedValue註解中指定的屬性的值。它創建了一個序列 |
@TableGenerator | 指定在@GeneratedValue批註指定屬性的值發生器。它創造了的值生成的表 |
@AccessType | 這種類型的註釋用於設置訪問類型。如果設置@AccessType(FIELD),則可以直接訪問變數並且不需要getter和setter,但必須為public。如果設置@AccessType(PROPERTY),通過getter和setter方法訪問Entity的變數 |
@JoinColumn | 指定一個實體組織或實體的集合。這是用在多對一和一對多關聯 |
@UniqueConstraint | 指定的欄位和用於主要或輔助表的唯一約束 |
@ColumnResult | 參考使用select子句的SQL查詢中的列名 |
@ManyToMany | 定義了連接表之間的多對多一對多的關係 |
@ManyToOne | 定義了連接表之間的多對一的關係 |
@OneToMany | 定義了連接表之間存在一個一對多的關係 |
@OneToOne | 定義了連接表之間有一個一對一的關係 |
@NamedQueries | 指定命名查詢的列表 |
@NamedQuery | 指定使用靜態名稱的查詢 |
4. JPA入門
加入依賴jpa、mysql、druid(非必須,自帶有連接池)、lombok(非必須)。
配置 application.yml
spring: datasource: url : jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8 type : com.alibaba.druid.pool.DruidDataSource username : xxx password : xxxx driver-class-name : com.mysql.cj.jdbc.Driver jpa: hibernate: # create:每次載入hibernate時都會刪除上一次的生成的表,然後根據你的model類再重新來生成新表。 # create-drop:每次載入hibernate時根據model類生成表,但是sessionFactory一關閉,表就自動刪除。 # validate:每次載入hibernate時,驗證創建資料庫表結構,只會和資料庫中的表進行比較,不會創建新表,但是會插入新值。 # update:最常用的屬性,第一次載入hibernate時根據model類會自動建立起表的結構(前提是先建立好資料庫),以後載入hibernate時根據model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要註意的是當部署到伺服器後,表結構是不會被馬上建立起來的,是要等應用第一次運行起來後才會。 ddl-auto: update show-sql: true
實體類
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Person { @Id @GeneratedValue private Long id; @Column(name = "name", nullable = true, length = 20) private String name; @Column(name = "age", nullable = true, length = 4) private int age; @Column(name = "password", nullable = true, length = 20) private String password; @OneToMany(mappedBy = "personId", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List<Dog> dogs; }
@Entity @Data @NoArgsConstructor @AllArgsConstructor public class Dog { @Id @GeneratedValue private Long id; private String name; private Long personId; }
5.自定義查詢
- 我們dao 需要繼承 JpaRepository<Person, Long>,Person 為實體類,Long為主鍵類型。這個類的內部已經定義好了單表數據操作語句,可以直接使用。但是有的時候,根據業務需求需要自定義一些數據操作,這就需要瞭解一下方法命名規則,根據方法名會自動生成相應的JPQL。
關鍵詞 | 樣例 | SQL符號 | 對應JPQL 語句片段 |
---|---|---|---|
And | findByLastnameAndFirstname | and | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | or | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | = | … where x.firstname = ?1 |
Between | findByStartDateBetween | between xxx and xxx | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | < | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | <= | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | > | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | >= | … where x.age >= ?1 |
After | findByStartDateAfter | > | … where x.startDate > ?1 |
Before | findByStartDateBefore | < | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | is null | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | is not null | … where x.age not null |
Like | findByFirstnameLike | like | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | not like | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | like 'xxx%' | … where x.firstname like ?1(parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | like 'xxx%' | … where x.firstname like ?1(parameter bound with prepended %) |
Containing | findByFirstnameContaining | like '%xxx%' | … where x.firstname like ?1(parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | order by | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | <> | … where x.lastname <> ?1 |
In | findByAgeIn(Collection |
in() | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection |
not in() | … where x.age not in ?1 |
TRUE | findByActiveTrue() | =true | … where x.active = true |
FALSE | findByActiveFalse() | =false | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | upper(xxx)=upper(yyyy) | … where UPPER(x.firstame) = UPPER(?1) |
6. 數據訪問層介面
除了使用按照規則定義方法為,還可以使用 JPQL。
查詢使用
@Query
,如果使用原生sql語句,需要使用@Query(value="原生sql",nativeQuery=true)
。對錶更新和刪除需要使用
@Modifying、@Transactional、@Query
的註解組合。對於
@Transactional
來說,readOnly=true
表明所註解的方法或類只是讀取數據。預設值就是這個。readOnly=false
表明所註解的方法或類是增加,刪除,修改數據。- 事物隔離級別
- 隔離級別是指若幹個併發的事務之間的隔離程度。TransactionDefinition 介面中定義了五個表示隔離級別的常量:
- TransactionDefinition.ISOLATION_DEFAULT:這是預設值,表示使用底層資料庫的預設隔離級別。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:這是事務最低的隔離級別。它允許令外一個事務可以看到這個事務未提交的數據,這種隔離級別會產生臟讀,不可重覆讀和幻像讀。
- TransactionDefinition.ISOLATION_READ_COMMITTED:保證一個事務修改的數據提交後才能被另外一個事務讀取。避免臟讀。但是不可重覆讀和幻讀有可能發生
- TransactionDefinition.ISOLATION_REPEATABLE_READ:避免臟讀和不可重覆讀,但是幻讀有可能發生。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重覆讀以及幻讀。但是這將嚴重影響程式的性能。通常情況下也不會用到該級別。
- 事務傳播行為
- 事務傳播行為指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。TransactionDefinition定義了七種傳播行為:
- TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。這是預設值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於 TransactionDefinition.PROPAGATION_REQUIRED。
在進行分頁的時候,在方法最後加上一個參數 Pageable。
// JpaRepository 中已經定義有關於單表的操作方法
public interface PersonRepository extends JpaRepository<Person, Long> {
//查詢指定用戶姓名的用戶
public Person findByName(String name);
//查詢指定用戶姓名的用戶
public Person findByNameAndPassword(String name, String password);
//查詢包含指定名字的用戶
public List<Person> findByNameContaining(String name);
// 排序查詢,返回list集合。Sort 在controller層中使用 new Sort(Sort.Direction.fromString("desc"),"id")
public List<Person> findByNameContaining(String name, Sort sort);
//分頁查詢, 查詢計算元素總個數、總頁數,數據多的情況下,代價是昂貴的。Pageable 在controller層中使用PageRequest.of(page, size,new Sort(Sort.Direction.fromString("desc"),"id"))
public Page<Person> findByNameContaining(String name , Pageable pageable);
//分頁查詢,返回的是一個片段,它只知道下一片段或者上一片段是否可用。
public Slice<Person> getByNameContaining(String name, Pageable pageable);
//查詢指定用戶姓名的用戶。使用 :name 作為占位符,需要使用 @Param 註解
@Query("select p from Person p where p.name = :name")
public Person getPerson(@Param("name") String name);
//用戶登錄驗證。使用方法中的參數位置作為占位符
@Query("select p from Person p where p.name = ?1 and p.password= ?2")
public Person login(String name, String password);
//模糊查詢用戶名裡面包含指定字元
@Query("select p from Person p where p.name like %:name%")
public List<Person> getNamesLike(@Param("name") String name);
//查詢密碼位數是5位數的全部用戶,使用mysql原始sql語句進行查詢
@Query(value="select * from person where length(password)=5",nativeQuery=true)
public List<Person> getPasswordisFive();
@Modifying
@Transactional(readOnly = true)
@Query("update Person p set p.name=?2 where p.id=?1")
public int UpdateName(Long id, String name);
@Modifying
@Transactional
@Query("delete from Person p where p.name=?1")
public int DeleteName(String name);
//查詢指定用戶名稱,按照id降序排序第一條記錄
Person findFirstByNameOrderByIdDesc(String name);
//模糊查詢指定用戶名稱,按照id降序排序前10條記錄
public List<Person> findFirst10ByNameLikeOrderByIdDesc(String name);
//查詢指定用戶名稱,按照id升序排序第一條記錄
Person findTopByNameOrderByIdAsc(String name);
//模糊查詢指定用戶名稱,按照id升序排序前10條記錄
public List<Person> findTop10ByNameLikeOrderByIdAsc(String name);
@Query("select p from Person p join p.dogs d where p.id = ?1")
public Person findPerson(Long id);
}
二、使用 Thymeleaf 模版引擎
介紹:Thymeleaf是xml/xhtml/html5的模板引擎。Thymeleaf 開箱即用的特性。它提供標準和spring標準兩種方言,可以直接套用模板實現JSTL、 OGNL表達式效果,避免每天套模板、改jstl、改標簽的困擾。同時開發人員也可以擴展和創建自定義的方言。Thymeleaf 提供spring標準方言和一個與 SpringMVC 完美集成的可選模塊,可以快速的實現表單綁定、屬性編輯器、國際化等功能。
集成 Thymeleaf
添加依賴 thymeleaf
修改配置 application.yml
spring: #開始thymeleaf設置 thymeleaf: #禁用模板緩存 cache: false
為模版註入數據
String message = "Hello, Thymeleaf!"; model.addAttribute("message", message); User u = new User(); u.setId(1L); u.setName("德魯伊"); u.setAge(18); model.addAttribute("user", u); Map<String,Object> map=new HashMap<>(); map.put("src1","1.jpg"); map.put("src2","2.jpg"); model.addAttribute("src", map); List<User> list=new ArrayList<User>(); list.add(u1); list.add(u2); model.addAttribute("userList", list); model.addAttribute("href", "http://www.ujiuye.com"); model.addAttribute("flag", "yes"); model.addAttribute("menu", "admin"); model.addAttribute("manager", "manager"); //日期時間 Date date = new Date(); model.addAttribute("date", date); //小數的金額 double price=128.5678D; model.addAttribute("price", price); //定義大文本數據 String str="Thymeleaf是Web和獨立環境的現代伺服器端Java模板引擎,能夠處理HTML,XML,JavaScript,CSS甚至純文本。\r\n" + "Thymeleaf的主要目標是提供一種優雅和高度可維護的創建模板的方式。為了實現這一點,它建立在自然模板的概念上,將其邏輯註入到模板文件中,不會影響模板被用作設計原型。這改善了設計的溝通,彌補了設計和開發團隊之間的差距。\r\n" + "Thymeleaf也從一開始就設計了Web標準 - 特別是HTML5 - 允許您創建完全驗證的模板,如果這是您需要的\r\n" ; model.addAttribute("strText", str); //定義字元串 String str2="JAVA-offcn"; model.addAttribute("str2", str2);
在 resource/templates 下創建模版 index.html
<!doctype html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="#{title}"></title> </head> <body> <h1 th:text="${message}"></h1> <hr/> </br> <img th:src="${src.src1}"> </br> <img th:src="${src.src2}"/> </br> <span th:text="${user.id}"></span> <span th:text="${user.name}"></span> <span th:text="${user.age}"></span> <hr/> <table> <tr th:each="user,iterStat : ${userList}"> <td th:text="${user.id}"></td> <td th:text="${user.name}"></td> <td th:text="${user.age}"></td> <td th:text="${iterStat.index}">index</td> <td th:text="${iterStat.count}">index</td> </tr> </table> <hr/> <!-- 給標簽賦值 th:text --> <h1 th:text="${userName}"></h1> <!-- 給屬性賦值 th:value、th:屬性名稱 --> <input type="text" name="names" th:value="${userName}"/> </br> <em th:size="${userName}"></em> <!-- 字元串拼接 --> <span th:text="'歡迎來:'+${userName}+'學習!'"></span> </br> <!-- 字元串拼接,方式2 --> <a th:href="${href}"><span th:text="|歡迎來:${userName}學習!|"></span></a> <hr/> <!-- th:if 條件成立就顯示 --> <h1 th:if="${flag=='yes'}" >中公教育</h1> <!-- th:unless 條件不成立就顯示 --> <h1 th:unless="${flag=='no'}" >優就業</h1> <!-- switch選擇語句 --> <div th:switch="${menu}"> <p th:case="'admin'">User is an administrator</p> <p th:case="${manager}">User is a manager</p> </div> <hr/> <!-- 把片段的內容插入到當前位置 --> <div th:insert="~{footer :: copy}"></div> </br> <!-- 使用片段的內容替換當前標簽 --> <div th:replace="~{footer :: copy}"></div> </br> <!-- 保留自己的主標簽,不要片段的主標簽 (官方3.0後不推薦) --> <div th:include="~{footer :: copy}"></div> <hr/> 時間:<span th:text="${#dates.format(date,'yyyy-MM-dd HH:mm:ss')}">4564546</span></br> 金額:<span th:text="'¥'+${#numbers.formatDecimal(price, 1, 2)}">180</span> </br> <!-- # 這裡的含義是 如果 atc.text 這個變數多餘200個字元,後面顯示... --> <p th:text="${#strings.abbreviate(strText,60)}">內容內容內容</p> <!-- 判斷字元串是否為空 --> <span th:if="${!#strings.isEmpty(str2)}">字元串str2不為空</span></br> <!-- 截取字元串,指定長度 --> <span th:text="${#strings.substring(str2,0,4)}">字元串str2的值</span> <hr/> <form class="form-horizontal" th:action="@{/manageruser/edit}" th:object="${user}" method="post"> <input type="hidden" name="id" th:value="*{id}" /> <div class="form-group"> <label for="name" class="col-sm-2 control-label">name</label> <div class="col-sm-10"> <input type="text" class="form-control" name="name" id="name" th:value="*{name}" placeholder="name"/> </div> </div> <div class="form-group"> <label for="age" class="col-sm-2 control-label">age</label> <div class="col-sm-10"> <input type="text" class="form-control" name="age" id="age" th:value="*{age}" placeholder="age"/> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <input type="submit" value="Submit" class="btn btn-info" /> <a href="/manageruser/" th:href="@{/manageruser/}" class="btn btn-info">Back</a> </div> </div> </form> </body> </html>
<!-- footer.html--> <body> <h1 th:fragment="copy"> © 1999-2018 Offcn.All Rights Reserved </h1> </body>