Spring Boot构建的Web应用中,基于MYSQL数据库的几种数据库连接方式进行介绍。
包括JDBC、JPA、MyBatis、多数据源和事务。
Spring Boot的Web应用中使用Mysq数据库,也充分展示Spring Boot的优势(尽可能少的代码和配置)。数据访问层我们将使用Spring Data JPA和Hibernate(JPA的实现之一)。
注意:如果你想JDBC和JPA可以一起使用,Spring Boot 是支持的,你只需要把JDBC和JPA的依赖都添加在pom.xml 中即可。无需其他特殊处理。
1、JPA
Spring Data Jpa 极大简化了数据库访问层代码,只要3步,就能搞定一切
1. 添加pom.xml依赖
2. 配置数据库连接
3. 编写Entity类,依照JPA规范,定义实体
4. 编写Repository接口,依靠SpringData规范,定义数据访问接口(注意,只要接口,不需要任何实现)
5. 写一小陀配置文件 (Spring Scheme配置方式极大地简化了配置方式)
1.Maven pom.xml文件
要使用MySQL,需要引入对应的connector,因此,首先在pom文件中添加如下依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
2.属性配置文件application.properties
在src/main/resources/application.properties中设置数据源和jpa配置。
spring.datasource.url = jdbc:mysql://localhost:3306/test
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
全部的配置都在如上的文件中了,不需要另外的XML配置和Java配置。
上文中的数据库配置,你需要换成你的数据库的地址和用户名密码。
hibernate的ddl-auto=update配置表名,数据库的表和列会自动创建(根据Java实体的熟悉), 这里 可以看到更多得hibernate配置。
spring.jpa.hibernate.ddl-auto=create-drop
。
spring.jpa.show-sql = true
来显示自动创建表的SQL语句,通过
spring.jpa.database = MYSQL
指定具体的数据,如果不明确指定Spring boot会根据classpath中的依赖项自动配置。
在Spring项目中,如果数据比较简单,我们可以考虑使用JdbcTemplate,而不是直接定义Datasource,配置jdbc的代码如下:
@Autowired
private JdbcTemplate jdbcTemplate;
只要定义了上面这个代码,Spring Boot会自动创建一个Datasource对象,然后再创建一个jdbctemplate对象来管理datasource,通过jdbctemplate操作数据库可以减少大量模板代码。如果你对SpringBoot的原理感兴趣,可以在org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类中查看其具体实现。
3.实体
package com.city.data.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Table(name = "city")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private int cityId;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private int provinceId;
public City() {
}
public City( int cityId) {
super();
this.cityId = cityId;
}
public int getCityId() {
return cityId;
}
public void setCityId(int id) {
this.cityId = id;
}
......
}
4.实体的数据访问层Dao
package com.city.data.service;
/**
*DAO层
*/
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import com.city.data.domain.City;
interface CityRepository extends Repository<City, Long> {
City findByName(String name);
}
我们都没有写一行SQL语句,也没有在代码中涉及到数据库连接、建立查询等方面的内容。只有实体类上的各种注解表明我们在于数据库做交互:@Entity,@Repository,@Id,@GeneratedValue,@ManyToOne,@ManyToMany以及@OneToMany,这些注解属于Java Persistance API。我们通过Respository接口的子接口与数据库交互,同时由Spring建立对象与数据库表、数据库表中的数据之间的映射关系。下面依次说明这些注解的含义和使用:
- @Entity,说明被这个注解修饰的类应该与一张数据库表相对应,表的名称可以由类名推断,当然了,也可以明确配置,只要加上
@Table(name = "books")
即可。需要特别注意,每个Entity类都应该有一个protected访问级别的无参构造函数,用于给Hibernate提供初始化的入口。 - @Id and @GeneratedValue:@Id注解修饰的属性应该作为表中的主键处理、@GeneratedValue修饰的属性应该由数据库自动生成,而不需要明确指定。
- @ManyToOne, @ManyToMany表明具体的数据存放在其他表中,在这个例子里,书和作者是多对一的关系,书和出版社是多对一的关系,因此book表中的author和publisher相当于数据表中的外键;并且在Publisher中通过@OneToMany(mapped = "publisher")定义一个反向关联(1——>n),表明book类中的publisher属性与这里的books形成对应关系。
- @Repository 用来表示访问数据库并操作数据的接口,同时它修饰的接口也可以被component scan机制探测到并注册为bean,这样就可以在其他模块中通过@Autowired织入。
5.业务逻辑层service
接口放在src\main\java\com\city\data\service包中,具体代码如下:
package com.city.data.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.city.data.domain.City;
@Component("cityService")
@Transactional
public class CityServiceImpl implements CityService {
private final CityRepository cityRepository;
@Autowired
public CityServiceImpl(CityRepository cityRepository) {
this.cityRepository = cityRepository;
}
//@Override
public City getCity(String cityName) {
Assert.notNull(cityName, "Name must not be null");
return this.cityRepository.findByName(cityName);
}
}
6.测试的控制器CityController
新建一个查询控制器CityController,
接口放在src\main\java\com\city\data\web包中,具体代码如下:
package com.city.data.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import com.city.data.service.CityService;
import com.city.data.domain.City;;
//原始格式
//@Controller
//json格式
@RestController
public class CityController {
@Autowired
private CityService cityService;
@RequestMapping("/")
@ResponseBody
@Transactional(readOnly = true)
public int getBeijing() {
//return "helloWorld";
return this.cityService.getCity("beijing").getCityId();
}
/**
* @PathVariable是用来获得请求url中的动态参数的
* @param name
* @return
*/
@RequestMapping(value = "/{name}", method = RequestMethod.GET)
public Map<String, Object> getCity( @PathVariable String name) {
City city = this.cityService.getCity(name);
Map<String, Object> response = new LinkedHashMap<>();
response.put("msg", "get city with name(" + name +")");
response.put("city", city);
return response;
}
}
你可以使用浏览器访问url http://127.0.0.1:8080
2、JDBC
JDBC 连接数据库
1、属性配置文件(application.properties)
如果使用JNDI,则可以替代 spring.datasource 的 url、username、password,如:
值得一提的是,无论是Spring Boot默认的DataSource配置还是你自己的DataSource bean,都会引用到外部属性文件中的属性配置。所以假设你自定义的DataSource bean,你可以在定义bean时设置属性,也可以在属性文件中,以“spring.datasource.*”的方式使属性配置外部化。
2、pom.xml 配置maven依赖
3、Java代码范例
CityServiceImpl.java
注意,使用jdbc要有变量
@Autowired
private JdbcTemplate jdbcTemplate;
<pre name="code" class="java">/*
*/
package com.city.data.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import com.city.data.domain.City;
import com.city.data.common.JdbcSQL;
@Component("cityService")
@Transactional
public class CityServiceImpl implements CityService {
private final CityRepository cityRepository;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
public CityServiceImpl(CityRepository cityRepository) {
this.cityRepository = cityRepository;
}
/**
*
*/
public List<City> getCityList(CitySearchCriteria criteria){
String sql = "SELECT * FROM city";
String cityName = criteria.getName();
int provinceid = criteria.getProvinceId();
String where = "";
if(provinceid > 0 ) {
where += " province_id=" + provinceid;
}
if(StringUtils.hasLength(cityName) ) {
where += " name=" + cityName;
}
if(!where.isEmpty() ) {
sql =sql + " where " + where;
}
return (List<City>) jdbcTemplate.query(sql, new RowMapper<City>(){
@Override
public City mapRow(ResultSet rs, int rowNum) throws SQLException {
City city = new City();
city.setCityId(rs.getInt("city_id"));
city.setName(rs.getString("name"));
city.setMap(rs.getString("map"));
city.setProvinceId(rs.getInt("province_id"));
return city;
}
});
}
public List getCityList2(Map<String, Object> condition){
JdbcSQL jdbcSQL =new JdbcSQL();
jdbcSQL.setTable("city");
jdbcSQL.setConditon(condition);
return jdbcSQL.selectByCondition();
}
public List<Map<String, Object>> getCityList2(Map<String, Object> condition) {
String sql = "select * from city";
return jdbcTemplate.queryForList(sql);
}
}
然后启动项目,访问地址: http://localhost:8080/
连接池说明
Tomcat7之前,Tomcat本质应用了DBCP连接池技术来实现的JDBC数据源,但在Tomcat7之后,Tomcat提供了新的JDBC连接池方案,作为DBCP的替换或备选方案,解决了许多之前使用DBCP的不利之处,并提高了性能。详细请参考:http://wiki.jikexueyuan.com/project/tomcat/tomcat-jdbc-pool.html
Spring Boot为我们准备了最佳的数据库连接池方案,只需要在属性文件(例如application.properties)中配置需要的连接池参数即可。
我们使用Tomcat数据源连接池,需要依赖tomcat-jdbc,只要应用中添加了spring-boot-starter-jdbc 或 spring-boot-starter-data-jpa依赖,则无需担心这点,因为将会自动添加 tomcat-jdbc 依赖。
假如我们想用其他方式的连接池技术,只要配置自己的DataSource bean,即可覆盖Spring Boot的自动配置。
请看我的数据源配置:
配置过连接池的开发人员对这些属性的意义都有所认识。
我们打开DEBUG日志输出,logback.xml 中添加:
- 1
然后启动项目,注意观察日志输出,如下图中会显示自动启用了连接池:
我在上面的数据源配置中添加了过滤器,并设置了延迟时间为0(故意设置很低,实际项目中请修改):
- 1
这个时候,我们访问 http://localhost:8080/myspringboot/stu/list 观察日志,会发现框架自动将大于该时间的数据查询进行警告输出,如下:
问题:
1、连接池的连接在最大生存时间 时间内没有请求数据库,再次使用的连接就报错。
spring.datasource.max-lifetime=86400000 (单位 毫秒)
2、如果连接池的连接长时间不使用(可能时间短于max-lifetime),但是超过mysql的wait_timeout的时间,mysql也会进行回收这个连接的。
my.cnf
wait_timeout=31536000
interactive_timeout=31536000
Mysql服务器默认的“wait_timeout”是8小时【也就是默认的值默认是28800秒】,也就是说一个connection空闲超过8个小时,Mysql将自动断开该connection,通俗的讲就是一个连接在8小时内没有活动,就会自动断开该连接。 wait timeout的值可以设定,但最多只能是2147483,不能再大了。也就是约24.85天 .
感谢您的支持,我会继续努力的! 扫码打赏,你说多少就多少