Spring Boot(3)-使用mysql

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hguisu/article/details/50977180

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配置。

如果希望通过Hibernate依靠Entity类自动创建数据库和数据表,则还需要加上配置项—— spring.jpa.hibernate.ddl-auto=create-drop
PS:在生产环境中不要使用create-drop,这样会在程序启动时先删除旧的,再自动创建新的,最好使用update;还可以通过设置 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.实体

我们本例是一个城市相关系统:
数据库中应该具备以下领域对象(domain object)
    创建一个City实体。User实体和Mysql数据库的city表相对应。
首先在src\main\java\com\city\data\下建立包domain,然后再在这个包下建立相应的实体类

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

创建完实体类,还需要创建CityRepository接口,该接口继承自Repository,这个接口放在src\main\java\com\city\data\service包中,具体代码如下:
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织入。
      我们可以添加自定义的接口函数,JPA会提供对应的SQL查询,例如,在本例中的CityRepository中可以增加findByName(String name)函数,JPA会自动创建对应的SQL查询——根据name查询城市,这种将方法名转换为SQL语句的机制十分方便且功能强大,例如你可以增加类似findByNameIgnoringCase(String name)这种复杂查询。


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)

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driverClassName=com.mysql.jdbc.Driver

如果使用JNDI,则可以替代 spring.datasource 的 url、username、password,如:

spring.datasource.jndi-name=java:tomcat/datasources/example 


值得一提的是,无论是Spring Boot默认的DataSource配置还是你自己的DataSource bean,都会引用到外部属性文件中的属性配置。所以假设你自定义的DataSource bean,你可以在定义bean时设置属性,也可以在属性文件中,以“spring.datasource.*”的方式使属性配置外部化。

2、pom.xml 配置maven依赖

        <!-- MYSQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
         </dependency>
         <!-- Spring Boot JDBC -->
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
          </dependency>


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的自动配置。

请看我的数据源配置:

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
spring.datasource.validation-query=SELECT 1
spring.datasource.test-on-borrow=false
spring.datasource.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=18800
spring.datasource.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=0)

配置过连接池的开发人员对这些属性的意义都有所认识。

我们打开DEBUG日志输出,logback.xml 中添加:

<logger name="org.springframework.boot" level="DEBUG"/>
  • 1

然后启动项目,注意观察日志输出,如下图中会显示自动启用了连接池: 
 
我在上面的数据源配置中添加了过滤器,并设置了延迟时间为0(故意设置很低,实际项目中请修改):

spring.datasource.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=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天 .



感谢您的支持,我会继续努力的! 扫码打赏,你说多少就多少

         

猜你喜欢

转载自blog.csdn.net/hguisu/article/details/50977180