基础15-访问数据库

如果Spring Boot整合最基本的JDBC的话,则需要经过如下步骤:

第一步:引入对应的starter、驱动:

<dependency>

  <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-starter-jdbc</artifactId>

</dependency>

<dependency>

  <groupId>mysql</groupId>

  <artifactId>mysql-connector-java</artifactId>

 </dependency>

第二步:参考官方文档,配置数据库连接信息:

spring.datasource.username=root

spring.datasource.password=MyPass@123

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

DataSourceConfiguration

该类会根据不同的properties配置,返回不同的DataSource:

①spring.datasource.tomcat则返回org.apache.tomcat.jdbc.pool.DataSource;

②spring.datasource.hikari则返回com.zaxxer.hikari.HikariDataSource;

③spring.datasource.dbcp2则返回org.apache.commons.dbcp2.BasicDataSource;

④还可以自定义DataSource;

DataSourceInitializerInvoker

该类实现了ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean接口;

查看InitializingBean.afterPropertiesSet方法:

@Override

public void afterPropertiesSet() {

DataSourceInitializer initializer = getDataSourceInitializer();

if (initializer != null) {

boolean schemaCreated = this.dataSourceInitializer.createSchema();

if (schemaCreated) {

initialize(initializer);

}

}

}

然后调用createSchema()方法:

/**

 * Create the schema if necessary.

 * @return {@code true} if the schema was created

 * @see DataSourceProperties#getSchema()

 */

boolean createSchema() {

List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");

if (!scripts.isEmpty()) {

if (!isEnabled()) {

logger.debug("Initialization disabled (not running DDL scripts)");

return false;

}

String username = this.properties.getSchemaUsername();

String password = this.properties.getSchemaPassword();

runScripts(scripts, username, password);

}

return !scripts.isEmpty();

}

然后initialize()调用initSchema()方法:

/**

 * Initialize the schema if necessary.

 * @see DataSourceProperties#getData()

 */

void initSchema() {

List<Resource> scripts = getScripts("spring.datasource.data", this.properties.getData(), "data");

if (!scripts.isEmpty()) {

if (!isEnabled()) {

logger.debug("Initialization disabled (not running data scripts)");

return;

}

String username = this.properties.getDataUsername();

String password = this.properties.getDataPassword();

runScripts(scripts, username, password);

}

}

这里,有2个方法频繁出现:getScripts、runScripts:

private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {

if (resources != null) {

return getResources(propertyName, resources, true);

}

String platform = this.properties.getPlatform();

List<String> fallbackResources = new ArrayList<>();

fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");

fallbackResources.add("classpath*:" + fallback + ".sql");

return getResources(propertyName, fallbackResources, false);

}

 

 

private void runScripts(List<Resource> resources, String username, String password) {

if (resources.isEmpty()) {

return;

}

ResourceDatabasePopulator populator = new ResourceDatabasePopulator();

populator.setContinueOnError(this.properties.isContinueOnError());

populator.setSeparator(this.properties.getSeparator());

if (this.properties.getSqlScriptEncoding() != null) {

populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name());

}

for (Resource resource : resources) {

populator.addScript(resource);

}

DataSource dataSource = this.dataSource;

if (StringUtils.hasText(username) && StringUtils.hasText(password)) {

dataSource = DataSourceBuilder.create(this.properties.getClassLoader())

.driverClassName(this.properties.determineDriverClassName()).url(this.properties.determineUrl())

.username(username).password(password).build();

}

DatabasePopulatorUtils.execute(populator, dataSource);

}

在createSchema的时候,getScripts方法的fallback=schema;

在initSchema的时候,getScripts方法的fallback=data;

再结合上面的红色、加粗字体,我们会发现如下情况:

classpath*:schema-${platform}.sql

classpath*:schema.sql

classpath*:data-${platform}.sql

classpath*:data.sql

其中的platform是来自于spring.datasource.platform配置,默认值为all,可以查看官网说明文档来从侧面证明我们的推测:

classpath:与classpath*:区别

classpath:表示只在指定的class路径中查找文件。如果有多个加载第一个。

classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找,有多少加载多少。

如何整合druid?

这个druid是阿里的产品,主要是提供了查询、监测等一系列服务,所以,这里才会选用该产品;

第一步:打开阿里仓库,找到druid的<dependency/>

<dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>druid</artifactId>

            <version>1.1.9</version>

</dependency>

第二步:在application.yml中配置属性信息;

想知道属性到底有哪些,则可以参考com.alibaba.druid.pool.DruidDataSource类,下面是该类的属性列表:


  private static final long serialVersionUID = 1L;
  
  private volatile long recycleErrorCount = 0L;
  
  private long connectCount = 0L;
  
  private long closeCount = 0L;
  
  private volatile long connectErrorCount = 0L;
  
  private long recycleCount = 0L;
  
  private long removeAbandonedCount = 0L;
  
  private long notEmptyWaitCount = 0L;
  
  private long notEmptySignalCount = 0L;
  
  private long notEmptyWaitNanos = 0L;
  
  private int keepAliveCheckCount = 0;
  
  private int activePeak = 0;
  
  private long activePeakTime = 0L;
  
  private int poolingPeak = 0;
  
  private long poolingPeakTime = 0L;
  
  private volatile DruidConnectionHolder[] connections;
  
  private int poolingCount = 0;
  
  private int activeCount = 0;
  
  private long discardCount = 0L;
  
  private int notEmptyWaitThreadCount = 0;
  
  private int notEmptyWaitThreadPeak = 0;
  
  private DruidConnectionHolder[] evictConnections;
  
  private DruidConnectionHolder[] keepAliveConnections;
  
  private volatile ScheduledFuture<?> destroySchedulerFuture;
  
  private DestroyTask destroyTask;
  
  private volatile Future<?> createSchedulerFuture;
  
  private CreateConnectionThread createConnectionThread;
  
  private DestroyConnectionThread destroyConnectionThread;
  
  private LogStatsThread logStatsThread;
  
  private int createTaskCount;
  
  private final CountDownLatch initedLatch = new CountDownLatch(2);
  
  private volatile boolean enable = true;
  
  private boolean resetStatEnable = true;
  
  private final AtomicLong resetCount = new AtomicLong();
  
  private String initStackTrace;
  
  private volatile boolean closing = false;
  
  private volatile boolean closed = false;
  
  private long closeTimeMillis = -1L;
  
  protected JdbcDataSourceStat dataSourceStat;
  
  private boolean useGlobalDataSourceStat = false;
  
  private boolean mbeanRegistered = false;
  
  public static ThreadLocal<Long> waitNanosLocal = new ThreadLocal<Long>();
  
  private boolean logDifferentThread = true;
  
  private volatile boolean keepAlive = false;
  
  private boolean asyncInit = false;
  
  protected boolean killWhenSocketReadTimeout = false;
  
  protected static final AtomicLongFieldUpdater<DruidDataSource> recycleErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "recycleErrorCount");
  
  protected static final AtomicLongFieldUpdater<DruidDataSource> connectErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "connectErrorCount");

下面则是application.yml配置文件信息:

spring:

  datasource:

    username: root

    password: MyPass@123

    url: jdbc:mysql://127.0.0.1:3306/spring?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false

    driver-class-name: com.mysql.cj.jdbc.Driver

    type: com.alibaba.druid.pool.DruidDataSource

    initialSize: 5

    minIdle: 5

    maxActive: 20

    maxWait: 60000

    timeBetweenEvictionRunsMillis: 60000

    minEvictableIdleTimeMillis: 300000

    validationQuery: SELECT 1 FROM orginfo

    testWhileIdle: true

    testOnBorrow: false

    testOnReturn: false

    poolPreparedStatements: true

    filters: stat,wall,log4j2

    maxPoolPreparedStatementPerConnectionSize: 20

    useGlobalDataSourceStat: true

    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

第三步:由于很多属性并不属于Spring中的属性,为了能够让这些属性起作用,就必须让我们自己来创建DataSource,通过手动创建来加载只属于DruidDataSource的属性并让属性生效;

第四步:为了让druid的监控统计功能生效,我们必须配置druid提供的servlet、filter

@Configuration

public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")

    @Bean

    public DataSource druid(){

        // 配置druid数据源,这个bean名字可以随便取,不影响后面

        return new DruidDataSource();

    }

    // 配置druid监控,需要配置一个管理后台的servlet,一个监控的filter

    @Bean

    public ServletRegistrationBean statViewServlet(){

        ServletRegistrationBean servlet = new ServletRegistrationBean(

                new com.alibaba.druid.support.http.StatViewServlet(),

                "/druid/*"

        );

        Map<String,String> initParam = new HashMap<>();

        initParam.put("loginUsername","root");// 配置用户名

        initParam.put("loginPassword","root");// 配置密码

        initParam.put("allow","");// 配置可以访问的IP地址,这里是允许所有IP访问

        servlet.setInitParameters(initParam);

        return servlet;

    }

    @Bean

    public FilterRegistrationBean webStatFilter(){

        FilterRegistrationBean filter = new FilterRegistrationBean();

        filter.setFilter(new com.alibaba.druid.support.http.WebStatFilter());

        Map<String,String> initParam = new HashMap<>();

        initParam.put("exclusions","*.js,*.css,*.jpg,*.png,/druid/*");

        filter.setInitParameters(initParam);

        filter.setUrlPatterns(Arrays.asList(new String[]{"/*"}));

        return filter;

    }

}

最后,查看效果:

如何整合Mybatis(注解版)

在整合了druid的基础上,继续整合Mybatis:

第一步:在POM中添加mybatis的启动器(自己去阿里maven上去搜即可):

<dependency>

            <groupId>org.mybatis.spring.boot</groupId>

            <artifactId>mybatis-spring-boot-starter</artifactId>

            <version>2.1.3</version>

</dependency>

第二步:基于@Mapper编写接口:

@Mapper

public interface OrgInfoMapper {

 

    @Update("update orginfo set orgname = #{orgname},areacode = #{areacode} where orgcode = #{orgcode}")

    public int updateOrgInfo(OrgInfo orgInfo);

 

    @Insert("insert into orginfo(orgcode,orgname,areacode) values(#{orgcode},#{orgname},#{areacode})")

    public int insertOrgInfo(OrgInfo orgInfo);

 

    @Select("select * from orginfo where orgcode = #{orgcode}")

    public OrgInfo getOrgInfoById(String orgcode);

 

    @Delete("delete from orginfo where orgcode = #{orgcode}")

    public int deleteOrgInfoById(String orgcode);

}

第三步:编写@Controller,这里图方便,直接使用@RestController:

@RestController

public class OrgInfoRestController {

 

    @Autowired

    private OrgInfoMapper orgInfoMapper;

 

    @GetMapping("/orginfo/{id}")

    public OrgInfo getOrgInfoById(

            @PathVariable

                    String id){

        System.out.println("id = " + id);

        return orgInfoMapper.getOrgInfoById(id);

    }

}

查看访问效果:

查看druid监控效果:

如何整合Mybatis(把注解版改造成XML版)

第一步:改造OrgInfoMapper类,把注解@Mapper、@Insert、Delete、Update、Select全部去掉;

把OrgInfoMapper类的全类名作为SQL映射文件<mapper/>的namespace属性值;

方法名分别作为<select、update、insert、delete/>的id属性值;

<?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.springboot.web.mapper.OrgInfoMapper">

    <select id="getOrgInfoById" resultType="com.springboot.web.entities.OrgInfo">

        select * from orginfo where orgcode = #{orgcode}

    </select>

 

    <update id="updateOrgInfo" >

        update orginfo set orgname = #{orgname},areacode = #{areacode} where orgcode = #{orgcode}

    </update>

 

    <insert id="insertOrgInfo" >

        insert into orginfo(orgcode,orgname,areacode) values(#{orgcode},#{orgname},#{areacode})

    </insert>

 

    <delete id="">

        delete from orginfo where orgcode = #{orgcode}

    </delete>

</mapper>

 

//@Mapper

public interface OrgInfoMapper {

 

//    @Update("update orginfo set orgname = #{orgname},areacode = #{areacode} where orgcode = #{orgcode}")

    public int updateOrgInfo(OrgInfo orgInfo);

 

//    @Insert("insert into orginfo(orgcode,orgname,areacode) values(#{orgcode},#{orgname},#{areacode})")

    public int insertOrgInfo(OrgInfo orgInfo);

 

//    @Select("select * from orginfo where orgcode = #{orgcode}")

    public OrgInfo getOrgInfoById(String orgcode);

 

//    @Delete("delete from orginfo where orgcode = #{orgcode}")

    public int deleteOrgInfoById(String orgcode);

}

第二步:配置mybatis全局文件:

mybatis:

  config-location: classpath:mybatis/mybatis-config.xml #指定全局配置文件路径

  mapper-locations: classpath:mybatis/mapper/*.xml #指定SQL映射文件路径

第三步:其他不用修改;

效果:

查看druid监控效果

注意:注解配置方式与XML配置方式是共存的,而不是互斥的,两种方式可以一起使用;

Mybatis拓展

先看一下MybatisAutoConfiguration的类声明:

@Configuration

@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})

@ConditionalOnSingleCandidate(DataSource.class)

@EnableConfigurationProperties({MybatisProperties.class})

@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})

public class MybatisAutoConfiguration implements InitializingBean {

 

}

再看看MybatisProperties的类声明:

@ConfigurationProperties(

    prefix = "mybatis"

)

public class MybatisProperties {

 

}

这里我们可以得知,mybatis的属性配置都是以“mybatis”开头的;

public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {

        this.properties = properties;

        this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();

        this.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable();

        this.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable();

        this.resourceLoader = resourceLoader;

        this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();

        this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();

}

 

 

public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

......

this.applyConfiguration(factory);

......

}

 

 

private void applyConfiguration(SqlSessionFactoryBean factory) {

        org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration();

        if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {

            configuration = new org.apache.ibatis.session.Configuration();

        }

 

        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {

            Iterator var3 = this.configurationCustomizers.iterator();

 

            while(var3.hasNext()) {

                ConfigurationCustomizer customizer = (ConfigurationCustomizer)var3.next();

                customizer.customize(configuration);

            }

        }

        factory.setConfiguration(configuration);

}

这里提到一个变量:List<ConfigurationCustomizer> configurationCustomizers;这里有个特殊地方在于:

import org.apache.ibatis.session.Configuration;

import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;

import org.springframework.context.annotation.Bean;

 

@org.springframework.context.annotation.Configuration

public class MyBatisConfig {

    /**

     *   org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer

     *   org.apache.ibatis.session.Configuration

     * */

    @Bean

    public ConfigurationCustomizer configurationCustomizer(){

         return new ConfigurationCustomizer(){

             @Override

             public void customize(Configuration configuration) {

                 /**

                  * 如果数据库某个字段修改了,可以使用这个属性

                  * 例如:orgname 改成了 org_name

                  * 这种情况下,通过setMapUnderscoreToCamelCase属性就可以不用修改Bean属性的情况下

                  * 来解决这种类型的改动

                  * */

                 configuration.setMapUnderscoreToCamelCase(true);

             }

         };

    }

}

通过自定义ConfigurationCustomizer,并添加到Spring容器中,这样就可以把自定义的ConfigurationCustomizer放入到configurationCustomizers变量中;

最后就是mybatis自身提供的@Mapper、@MapperScan、@MapperScans注解;

如何整合JPA

JPA是ORM框架(Object-Relational-Mapping)的一种,要想使用该框架,一共有5个步骤:

第一步:使用JPA的starter:

<dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

第二步:声明Entity与Table:

@Entity

@Table(name = "user_info")

public class Userinfo {

    /**

     * 下面的注解,属于JPA独有的注解,潜台词就是告诉Spring Boot,下面要使用JPA了

     *

     * @Entity标明该类是一个与数据表进行映射的实体类

     * @Table(name = "user_info")标明该类要映射的数据表名称是user_info

     *

     * @Id标明该属性是一个主键

     * @GeneratedValue(strategy = GenerationType.IDENTITY)标明该属性具有自增属性

     * @Column标明该属性要映射的数据表的column

     */

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Integer id;

 

    @Column(name = "cert_name", length = 50)

    private String certName;

 

    @Column(name = "cert_id")

    private String certId;

 

    @Override

    public String toString() {

        return "Userinfo{" +

                "id=" + id +

                ", certName='" + certName + '\'' +

                ", certId='" + certId + '\'' +

                '}';

    }

 

    public Integer getId() {

        return id;

    }

 

    public void setId(Integer id) {

        this.id = id;

    }

 

    public String getCertName() {

        return certName;

    }

 

    public void setCertName(String certName) {

        this.certName = certName;

    }

 

    public String getCertId() {

        return certId;

    }

 

    public void setCertId(String certId) {

        this.certId = certId;

    }

}

第三步:声明Repository:

public interface UserInfoRepository  extends JpaRepository<Userinfo,Integer> {

    /**

     * JpaRepository泛型的第一个参数指的是数据表对应的实体类

     * 第二个参数则是实体类对应的主键

     * 可以查看源代码下面的方法就能看出来:

     * T getOne(ID var1);

     * <S extends T> List<S> findAll(Example<S> var1);

     * */

}

第四步:配置application.yml文件:

Spring:

  jpa:

    hibernate:

      ddl-auto: update # 标明当实体类发生变化的时候,对应的数据表自动进行相应变化,表没有的时候创建,创建完了,除了进行修改也不再动了

    show-sql: true #显示SQL语句

第五步:编写controller:

@RestController

public class OrgInfoRestController {

 

    @Autowired

    private OrgInfoMapper orgInfoMapper;

 

    @Autowired

    UserInfoRepository userInfoRepository;

 

 

    @GetMapping("/orginfo/{id}")

    public OrgInfo getOrgInfoById(

            @PathVariable

                    String id){

        return orgInfoMapper.getOrgInfoById(id);

    }

 

    @GetMapping("/userinfo/{id}")

    public Userinfo getUserInfoById(

            @PathVariable

                    Integer id){

        Optional<Userinfo> userinfo = userInfoRepository.findById(id);

        return userinfo.get();

    }

 

    @GetMapping("/userinfo")

    public Userinfo insertUserInfoById(Userinfo userinfo){

        return userInfoRepository.save(userinfo);

    }

}

查看效果:

查看druid监控效果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值