本来是想着搞个数据库,里面存一些图片的地址、后缀等以及图集的标题、大小等信息的。后来发现最重要的图片的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,结果报空指针异常,则参考第四节。