Spring Boot + Java爬虫 + 部署到Linux(五、使用spring data JPA 实现数据访问层)

    本来是想着搞个数据库,里面存一些图片的地址、后缀等以及图集的标题、大小等信息的。后来发现最重要的图片的URL信息是动态变化的,而且我实在是看不出什么规律,也就说图片的源是有时效的,而且时效很短抓狂,所以存储意义就不大了。不过重在参与,而且也不是所有网站都这样,所以还是实现出来吧。

    首先呢,先要有个数据库吧。我使用的是mysql社区版,版本是5.7。安装mysql的时候,发现有时候出现安装程序的“next”等按钮没了,然后其实可以通过键盘按键x(表示执行,execute),b(返回上步,back),n(next,下一步)来实现。设置好密码,安装好服务器,安装好java-connector之类的,配好环境变量就行了。在命令行中输入mysql -hlocalhost -uroot -p,然后回车输入密码,能进去就行。先创建个数据库,比如叫ecrawl,windows下面mysql好像都不区分大小写的。

执行命令"create database ecrawl;",然后“use database ecrawl;"就进入数据库了。

    然后就是建表。先定义表结构吧。根据在第二节的网站为模板,我们就需要两个表,一个存储图集信息,一个存储图片信息,并且存在着一对多的关系。但是为了简化约束,就没有使用外键,对应的JPA中也没有添加一对多约束。图集的表呢,我们就叫gallery;图片的表呢,名字就叫image。然后gallery需要长度、URL、标题、序列号等信息,其中的序列号是网站确定图集的唯一性约束,可以通过URL得到。image需要URL、后缀名、所属图集的序列号、在图集中的位置等信息。据此,我们建表如下:

DROP TABLE IF EXISTS gallery;
CREATE TABLE gallery (
	id  bigint unsigned not null AUTO_INCREMENT,
    url varchar(100) not null, #URL
    serial_id varchar(20) not null, #编号
    title varchar(200) not null, #标题
    lenth  smallint unsigned not null, #长度/大小
    gmt_create datetime NOT NULL ,
	gmt_modified datetime NOT NULL ,
	unique(serial_id),
    primary key(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

    

DROP TABLE IF EXISTS image;
CREATE TABLE image (
	id  bigint unsigned not null AUTO_INCREMENT,
    url varchar(500) not null,
	inner_url varchar(100) not null,
    serial_num smallint unsigned not null,
    gallery_id varchar(20) not null,
    suffix varchar(10) not null,
    gmt_create datetime NOT NULL ,
	gmt_modified datetime NOT NULL ,
	primary key(id),
	CONSTRAINT uk_image UNIQUE (gallery_id,serial_num)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

在命令行上粘贴上这些,就能建表成功了。下面就是Java的操作了,怎么操作数据库,实现数据访问呢。

先做一些准备工作,因为要访问数据库和使用jpa,需要一些包。如果pom.xml没有,则添加上:

<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			</dependency>
          <!-- jpa-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency> 

其次呢,在项目的/src/main/resources下面新建一个application.properties文件,就是项目的配置文件。

spring.profiles.active=pro
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/ecrawl?characterEncoding=UTF-8&&useSSL=false
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect

    第一个那个就是生效的另外的配置文件,叫pro。所以我们再新建一个pro,其实springboot的配置文件很多用yml格式的,所以我们的pro就是用yml格式。在同级目录下创建application-pro.yml,然后写上(注意:后空格不能省略)

server:
    port: 8080
    context-path: /

    表示我们的网站上下文路径是”/“,就是根目录是这个,端口是8080。比如我们要访问一个相对路径是/index的地址,我们需要在浏览器上输入”http://localhost:8080/index“这个地址。如果context是/text,端口是80,那么我们只需要输入”http://localhost/test/index“。就是这个意思。当然也可以不用这个配置文件,直接把这个写到application.properties中也行。application.properties也可以改成yml格式的。

    返回刚才说的application.properties文件,通过这个active就可以指定不同的配置文件了,从而可以实现配置的切换。下面的那些就不多说了,都忘不了。注意的是useSSL,这个是和数据库通信的时候是否加密,加密可能会影响性能,但是安全。最下面的表示数据库用的是mysql5版本,不配置好像也行。其余的一些很多比如连接池之类的都没有配置,就都使用默认的了。


第一步,先建立对应的实体类

    我们起名为Gallery和Image,首先是Gallery:



import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity //表示是实体类
@Table(name = "gallery") //对应的表名
public class Gallery {
 
//    @Id //主键标识,主键类型要实现序列化接口
//    @GeneratedValue(strategy = GenerationType.IDENTITY) //由数据库自动生成
//    @Column(name = "id") //数据库字段名(属性、列名)
//    private Integer id;
//     
    @Column(name = "url")
    private String url;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;
	
	
	@Column(name = "serial_id",unique=true)
    private String serialId;
    
    @Column(name = "title")
    private String title;
    
    @Column(name = "lenth")
    private Integer lenth;
    
    @Column(name = "gmt_create")
    private Date gmtCreate;
    
    @Column(name = "gmt_modified")
    private Date gmtModified;
	
	

	

	public Integer getLenth() {
		return lenth;
	}

	public void setLenth(Integer lenth) {
		this.lenth = lenth;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getSerialId() {
		return serialId;
	}

	public void setSerialId(String serialId) {
		this.serialId = serialId;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	

	public Date getGmtCreate() {
		return gmtCreate;
	}

	public void setGmtCreate(Date gmtCreate) {
		this.gmtCreate = gmtCreate;
	}
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Date getGmtModified() {
		return gmtModified;
	}

	public void setGmtModified(Date gmtModified) {
		this.gmtModified = gmtModified;
	}
    
    
    
     
}

接下来是Image:


import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
 
@Entity //表示是实体类
//添加一个唯一性约束
@Table(name = "image",uniqueConstraints = {@UniqueConstraint(columnNames={"gallery_id", "serial_num"})}) //对应的表名
public class Image {
 
//    @Id //主键标识
//    @GeneratedValue(strategy = GenerationType.IDENTITY) //数据库自动生成
//    @Column(name = "id") //数据库字段名(属性、列名)
//    private Integer id;
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;
    
    @Column(name = "url")
    private String url;
    
    @Column(name = "serial_num")
    private Integer serialNum;
    
    @Column(name = "gallery_id")
    private String galleryId;
    
    @Column(name = "suffix")
    private String suffix;
    
    @Column(name = "gmt_create")
    private Date gmtCreate;

    @Column(name = "inner_url")
    private String innerUrl;

    @Column(name = "gmt_modified")
    private Date gmtModified;
    
	public String getInnerUrl() {
		return innerUrl;
	}

	public void setInnerUrl(String innerUrl) {
		this.innerUrl = innerUrl;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	

	

	public Integer getSerialNum() {
		return serialNum;
	}

	public void setSerialNum(Integer serialNum) {
		this.serialNum = serialNum;
	}

	public String getGalleryId() {
		return galleryId;
	}

	public void setGalleryId(String galleryId) {
		this.galleryId = galleryId;
	}

	public String getSuffix() {
		return suffix;
	}

	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

	public Date getGmtCreate() {
		return gmtCreate;
	}

	public void setGmtCreate(Date gmtCreate) {
		this.gmtCreate = gmtCreate;
	}

	@Override
	public String toString() {
		return galleryId+"/"+serialNum;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Date getGmtModified() {
		return gmtModified;
	}

	public void setGmtModified(Date gmtModified) {
		this.gmtModified = gmtModified;
	}
	
    
    
    
     
}

    实体类定义完毕了。

第二步:数据访问层

首先是GalleryDAO,注意泛型第一个是对应的实体类,第二个对应的是主键(必须实现序列化接口)

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import yly.crawl.springboot.pojo.Gallery;

public interface GalleryDAO extends JpaRepository<Gallery, Long>{
	@Query("select gallery from Gallery gallery where gallery.serialId=?1")
	List<Gallery> findBySerialId(String serialId);
	
}

ImageDAO:

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import yly.crawl.springboot.pojo.Image;

public interface ImageDAO extends JpaRepository<Image, Long> {
	@Query("select image from Image image where image.galleryId=?1 and image.serialNum>=?2 and image.serialNum<=?3 order by image.serialNum")
	List<Image> inImage(String galleryId, Integer beginSerialNum, Integer endSerialNum);
}

    为什么查询方法这么少呢?这是因为Spring data JPA已经实现了一些方法,包括常见的增删改操作。可以点开JpaRepository看看里面的方法。有什么find,save等,看名字就很容易明白是干啥的。当我们只需要简单的操作时,用这些默认实现就足够了。当我们需要自定义sql的时候,可以用@Query这种方式。可以看到注解里面的sql语句和mysql的sql语法大大不同。以ImageDAO里面的这个为例,小写的image,表示是大写的Image类的一个对象名。即image是Image的一个对象,然后where条件里,可以直接用Image对象的属性。?1这些是占位符,对应下面的方法的参数的位置,即?1对应第一个参数galleryId。而select的是image,最后就会自动封装成Image对象放到函数的返回List中。可能看起来麻烦,实际用起来就会感觉挺容易的。


    还有一点就是这是个接口,里面的方法都没有实现。这也是面向接口编程的一个例子。到后面我们使用的时候,会使用spring给我们自动输入好的对象,给我们操作。即使用注解@AutoWired GalleryDAO galleryDAO,这个时候galleryDAO就会被注入一个对象,然后我们就能使用它的方法了,比如自定义的查询galleryDAO.findBySerialId()。如果在websocket中使用autowired,结果报空指针异常,则参考第四节。


    最后解释一下,这两个自定义查询。findBySerialId,就是根据gallery的serialId来找到这个gallery。而inImage,就是看看这个gallery的从开始到结束总共有多少个已经存在数据库里面了。举个例子,比如参数galleryId是20231,begin是2,end是32,这个查询就是找到编号20231的图集的第2-32张图片的信息有多少个在数据库里。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值