一、项目目标
开发一个IP地址查询功能的REST-API程序,实现对指定IP地址的国家、省、市及运营商信息查询,如果没有参数指定具体Ip地址则自动将来访IP作为查询IP地址进行返回信息。
设计实现效果如下:
1、有查询ip参数指定情况
http://localhost:8080/getipinfo?ip=202.103.44.150
[{"id":1,"ipadd":"202.103.44.150","country":"中国","privince":"湖北省","city":"武汉市","isp":"电信"}]
2、无指定查询ip参数情况
http://localhost:8080/getipinfo
[{"id":1,"ipadd":"58.51.94.1","country":"中国","privince":"湖北省","city":"武汉市","isp":"电信"}]
二、涉及技术分析
1、数据库部分
数据库直接使用上一篇博文的成果,使用docker起一个mysql数据库,数据库名为ipdb,账号root,密码123456
2、springboot涉及数据库操作部分-spring data JPA
Spring Data JPA是更大的Spring Data系列的一部分,可以轻松实现基于JPA的存储库。此模块处理对基于JPA的数据访问层的增强支持。它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。
在相当长的一段时间内,实现应用程序的数据访问层一直很麻烦。必须编写太多样板代码来执行简单查询以及执行分页和审计。Spring Data JPA旨在通过减少实际需要的工作量来显着改善数据访问层的实现。作为开发人员,您编写存储库接口,包括自定义查找器方法,Spring将自动提供实现。
Spring Data存储库抽象中的中央接口是Repository
。它将域类以及域类的ID类型作为类型参数进行管理。此接口主要用作标记接口,用于捕获要使用的类型,并帮助您发现扩展此接口的接口。该CrudRepository
规定对于正在管理的实体类复杂的CRUD功能。
public interface CrudRepository extends Repository {
S save(S var1);
Iterable saveAll(Iterable var1);
Optional findById(ID var1);
boolean existsById(ID var1);
Iterable findAll();
Iterable findAllById(Iterable var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable extends T> var1);
void deleteAll();
}
这个接口实现了对数据库实体的增删改查,通过对该接口的实现与扩展,可以非常方便的实现对数据库表的操作。
3、JPA-Java Persistence API,java持久性API
JPA (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象/关联映射工具来管理 Java 应用中的关系数据。
JPA所维护的核心是entity,是通过一个持久化上下文来使用,包括下面三部分内容:
对象关系映射(ORM)支持注解或XML两种方式描述,springboot中通过注解实现;
entity操作API,对entity对象的crud操作,完成对象的持久化与查询;
查询语言,约定了JPQL(Java Persistence Query Language)java的类SQL查询语言,主体是entity对象。
4、关于spring.jpa.hibernate.ddl-auto 配置
Spring Boot 应用在启动过程中,就能根据实体,来自动映射成为数据库的表结构。这个选项在application属性文件中配置
建议该选型配置为update。Spring Boot会根据是否认为您的数据库是嵌入式的,为您选择一个默认值。create-drop
如果未检测到架构管理器或none
在所有其他情况下,默认设置为create-drop。
三、详细操作步骤
1、创建数据库
docker run -d --rm --name mysqlserver -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=ipdb -e TZ='Asia/Shanghai' -e MYSQL_ROOT_HOST="133.3.%" -p 3306:3306 mysqlutf8:v2
2、idea创建一个新的工程
使用spring initalizr向导创建工程
选择springboot的maven依赖包,至少包括web、jpa与mysql
在项目目录下新建三个目录controller、dao、entity
3、在pom.xml确认springboot依赖项
4、编辑定义application.properties属性文件,定义项目运行环境
spring.datasource.url = jdbc:mysql://133.3.5.211:3306/ipdb
spring.datasource.username = root
spring.datasource.password = 123456
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.jpa.database = MYSQL
# 在控制台显示自动生成的sql语句
spring.http.log-request-details=true
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
# 命名策略 ,将实体类转换为数据库表名,字段为小写,当有大写字母的时候会转换为分隔符号“_”。
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# RDBMS 方言, 这里选用MySQL5Dialect
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
5、建立Ipbase实体类
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "ipbases")
public class Ipbase {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@NotNull
private String ipadd;
@NotNull
private String country;
@NotNull
private String privince;
@NotNull
private String city;
@NotNull
private String isp;
public Ipbase() {
super();
}
public Ipbase(@NotNull String ipadd, @NotNull String country, @NotNull String privince, @NotNull String city, @NotNull String isp) {
this.ipadd = ipadd;
this.country = country;
this.privince = privince;
this.city = city;
this.isp = isp;
}
@Override
public String toString() {
return "Ipbase{" +
"id=" + id +
", ipadd='" + ipadd + '\'' +
", country='" + country + '\'' +
", privince='" + privince + '\'' +
", city='" + city + '\'' +
", isp='" + isp + '\'' +
'}';
}
{set and get ......}
创建完实体类就可以执行查看实体类自动在数据库中建表的效果
通过mysql登陆进数据库查看数据库中内容
@GeneratedValue(strategy = GenerationType.IDENTITY) 当ID字段的注释为IDENTITY类型时,数据库只生成一张实体类表格,如果数据库不支持这类字段比如oracle数据库时,就需要选择另外一种注释方法,@GeneratedValue(strategy = GenerationType.AUTO),此时如上图所示系统会自动产生两张表格,其中一张hibernate_sequence表格类似oracle的序列专门配合生成id序列号使用。
6、创建一个IpbaseDao的实体化接口实现接口,继承CrudRespositroy接口后再做一些个性化实现即可
import javax.transaction.Transactional;
import java.util.List;
@Transactional
public interface IpbaseDao extends CrudRepository {
public Ipbase findByIpadd(String ipadd);
@Override
List findAll();
}
我定义的ipbasedao接口扩展了crudrepository接口,原接口定义的两个泛型类型是实体类及其主键的类型,本例中实体类是Ipbase,主键ID是LONG类型。 这是一个神奇的接口,它提供所有CRUD实现。您无需在任何地方实现此接口。相反,Spring Boot将为您提供实现。
Create, read, update and delete (CRUD)针对数据库的增删改查自动搞定。
可以扩展CrudRepository,您只需添加一个新方法findAll返回一个列表。如下例:
@Repository
public interface DataRepository extends CrudRepository {
@Override
List findAll();
}
如果您有一个“抽象”存储库要由所有存储库扩展,您也可以添加此方法,因此它将对您的所有存储库生效。如下例:
@NoRepositoryBean
public interface GenericRepository extends CrudRepository {
@Override
List findAll();
}
7、在controller目录下新建一个控制类ipcontroller.java
import com.ipbase.info.dao.IpbaseDao;
import com.ipbase.info.entity.Ipbase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class ipcontroller {
@Autowired
IpbaseDao ipbaseDao;
@RequestMapping(value = "/add", produces = "text/html;charset=UTF-8", method = RequestMethod.GET)
public String addip(String ipadd, String country, String privince, String city, String isp, HttpServletRequest request) {
String ipId = "";
try {
Ipbase ipbase = new Ipbase(ipadd, country, privince, city, isp);
ipbaseDao.save(ipbase);
ipId = String.valueOf(ipbase.getId());
} catch (Exception ex) {
return "Add Ip Error:" + ex.toString();
}
return "Succesfully Add Ip Info with id=" + ipId;
}
@RequestMapping(value = "/getipinfo", produces = "text/html;charset=UTF-8", method = RequestMethod.GET)
public String findip(String ip, HttpServletRequest request) {
Ipbase ipbase = null;
try {
if (ip != null) {
ipbase = ipbaseDao.findByIpadd(ip);
} else {
ipbase = ipbaseDao.findByIpadd(request.getRemoteAddr());
}
} catch (Exception ex) {
return ex.toString();
}
if (ipbase!=null){
return ipbase.toString();
} else {
return "对不起数据库中没有这个IP:"+ip;
}
}
8、按shift+f9键进行项目测试观察底部console窗口输出日志
观察最后一行出现Started InfoApplication in 4.677 seconds (JVM running for 5.711)就表明项目启动成功
9、通过浏览器录入2条测试数据
http://133.3.5.71:8080/add?ipadd=133.3.5.71&country=%E4%B8%AD%E5%9B%BD&privince=%E6%B9%96%E5%8C%97&city=%E6%AD%A6%E6%B1%89&isp=%E7%94%B5%E4%BF%A1
10、通过数据库查看录入测试数据情况
11、通过浏览器进行REST风格查询IP地址
a、查询指定ip地址
http://133.3.5.71:8080/getipinfo?ip=202.103.44.150
b、不指定参数查询访问IP地址
http://133.3.5.71:8080/getipinfo
c、查询一个数据库中不存在的IP地址
http://133.3.5.71:8080/getipinfo?ip=1.1.1.1
四、总结回顾
有两年没有碰java开发了,在本次springboot的jpa开发过程中,又找回了一点感觉,觉得使用spring-boot-starter-data-jpa开发数据库应用真的比以前从entity-dao-services-controller简单了很多,以前开发中还需要自己手工建表,现在通过对entity的调整自动解决与数据库的交互问题,非常方便。
下图是执行查询过程中,日志输出的hibernate自动为我们生成的sql语句,方便我们排查故障,如果不想输出这些语句,可以通过spring.http.log-request-details=false来关闭。