春季靴 Crud存储库
在这篇文章中,我们’我将寻找如何创建和使用Spring Boot Crud存储库。
弹簧Boot Crud存储库
为要管理的实体类型提供复杂的CRUD功能。此接口主要用作标记接口,以捕获要使用的类型并帮助您发现扩展该接口的接口。它需要域类以及域类的id类型作为类型参数来管理。
Generally, to use up a Crud存储库
you’ll need a 数据base
和 弹簧Framework 支持s a wide number of them including the most common ones like SQL
, MySQL
, MongoDB
, H2 数据base
和 more. 让’那就继续吧。
使用的工具
- 爪哇 8+
- Maven 3+
- 春季靴(您选择的版本,我们’将会使用v2.2.2.RELEASE,但是这些概念对于2.x 弹簧Boot是有效的)
- 您选择的数据库(我们’ll be using MongoDB)
- 您选择的IDE(我们’将会使用Intellij IDea)
项目结构:
让’从示例开始。
获取文件->新建项目并添加必要的依赖项,您可以转到 弹簧 Starter IO 和
添加以下依赖项:
- 弹簧启动启动器网络
- 弹簧-boot-starter-data-mongodb
After that your pom.xml
would look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<父母> <groupId>组织.弹簧框架.开机</groupId> <artifactId>弹簧-开机-起动机-父母</artifactId> <版>2.2.2。发布</版> <relativePath/> <!- 抬头 父母 从 资料库 -> </父母> <依存关系> <依赖> <groupId>组织.弹簧框架.开机</groupId> <artifactId>弹簧-开机-起动机-网路</artifactId> </依赖> <依赖> <groupId>组织.弹簧框架.开机</groupId> <artifactId>弹簧-开机-起动机-数据-mongodb</artifactId> </依赖> </依存关系> <建立> <最后Name>${项目.artifactId}</最后Name> <外挂程式> <插入> <groupId>组织.弹簧框架.开机</groupId> <artifactId>弹簧-开机-专家-插入</artifactId> <处决> <执行> <ID>建立-信息</ID> <目标> <目标>建立-信息</目标> </目标> </执行> </处决> </插入> </外挂程式> <资源> <资源> <目录>src/主要/资源</目录> <过滤>真正</过滤> </资源> </资源> </建立> |
弹簧基本上是一个巨大的框架伞,它支持多个数据库的方式是利用入门
项目及其各自的自动配置。您可以阅读所有有关它们的信息
这里
的 起动机-parent
is how 弹簧 handles 依赖 management 和 solves a few of many problems with transitive 依存关系
连接MongoDB
有很多方法可以连接MongoDb。我们’ll be using the MongoURI method with 弹簧 autoconfiguration
by
setting the relevant properties in our application.properties
file 和 let 弹簧 handle everything else.
For 这个 all you need to define is the MongoURI
和 DBName
in your application.properties
file.
弹簧.data.mongodb.uri=mongodb://127.0.0.1/
but you can 加 all sorts of 加itional settings 这里 including 用户名
和 password
如果 your DB has them.
弹簧.data.mongodb.uri=mongodb://127.0.0.1:27017/springCRUDSample?username=TestUser&password=TestPassword&socketTimeoutMS=60000
阅读更多关于 这个.
储存库类
创建一个名为的新接口 用户资料库
1 2 3 4 5 6 |
@资料库 上市 接口 用户资料库 延伸 Crud存储库<UserDao, 串> { } |
的 用户资料库
would extend the Crud存储库<T, Y>
接口. Here ‘T’是需要在数据库中创建,读取,更新,删除的实体的类,以及‘Y’实体的ID字段的类型。
的 @资料库 annotation marks the 接口 as a 弹簧 Stereotype
和 弹簧 would provide 和 implementation for 这个 接口 at 跑time (reducing boiler-plate 数据base code) 阅读更多.
的 Crud存储库
has basic 创造, read, update, 删除 operation methods:
<S 延伸 T> S 保存(S entity);
将给定记录保存到数据库<S 延伸 T> 可迭代的<S> 保存All(Iterable<S> entities);
将多个记录保存到数据库。可选的<T> findById(ID ID);
查找与ID匹配的记录布尔值 existsById(ID ID);
检查记录是否存在提供的ID可迭代的<T> 找到所有();
从数据库获取所有记录可迭代的<T> 找到所有ById(Iterable<ID> IDs);
获取与提供的ID匹配的所有记录长 计数();
返回数据库中的记录数。虚空 删除ById(ID ID);
删除具有给定id的实体。虚空 删除(T entity);
删除给定的实体。虚空 删除All(Iterable<? 延伸 T> entities);
删除所有提供的记录。虚空 删除All();
从数据库中删除所有记录
大多数功能是不言自明的,我们’我将在此示例中查看其中的一些,但让’s first talk about
the 保存
和 保存All
method.
保存方法
的 reason why 保存
gets a whole section is because something that got to me as well when I was in the initial stages
with 弹簧Boot
.
的 保存
和 保存All
basically works as an upsert
(update or insert). If the object you pass in the 保存 method
has an ID
(annotated with the @Id
in MongoDB
UserDao) value, 弹簧 would understand that there seems to be
具有相同ID的数据库中的一条记录,如果没有,将尝试查找该记录以执行更新’t find it 弹簧
将创建一个新记录,并将id字段(在DB中)设置为对象的id字段中存在的值。在
case the ID
field is 空值 or empty, 弹簧 would treat it as a 新 record 和 perform the insert.
ID
field, 如果 lets say that you don’t have any ID field in your 数据base 类 (DAO) even 如果 you read the record 从 the DB 和 call the 保存()
method, 弹簧 would consider it as a 新 record 和 a Duplicate record would be 被创造. If you have any 独特 constraints in your 数据base 采集 they would fail 和 throw a DuplicateKeyException
用户实体类
的 UserDao
is the 类 that represents the 用户 record stored in the 数据base.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
@文件(采集 = “用户”) 上市 类 UserDao { @ID 私人的 串 用户身份; @索引(独特 = 真正) @领域(“用户名”) 私人的 串 用户名; @领域(“名字”) 私人的 串 名字; @领域(“姓”) 私人的 串 姓; 上市 UserDao() { } 上市 UserDao(UserDto 语境) { 这个.用户身份 = 语境.getUserId(); 这个.用户名 = 语境.getUsername(); 这个.名字 = 语境.getFirstName(); 这个.姓 = 语境.getLastName(); } 上市 UserDao UpdatedFrom(UserDto 语境) { 如果 (语境.getUsername() != 空值 && !语境.getUsername().修剪().是空的()) 这个.用户名 = 语境.getUsername(); 如果 (语境.getUsername() != 空值 && !语境.getUsername().修剪().是空的()) 这个.名字 = 语境.getFirstName(); 如果 (语境.getUsername() != 空值 && !语境.getUsername().修剪().是空的()) 这个.姓 = 语境.getLastName(); 返回 这个; } 上市 串 getUserId() { 返回 用户身份; } 上市 串 getUsername() { 返回 用户名; } 上市 串 getFirstName() { 返回 名字; } 上市 串 getLastName() { 返回 姓; } } |
该类包含一些字段,如userId,用户名,名字,姓氏。 用户身份是代表的字段
the ID
field of the 数据base 和 hence is annotated as @Id
(that’s how it’s done for MongoDB).
的 @Document
annotation is another 弹簧 Stereotype that marks the 类 as a 数据base 类 和 弹簧 would use that 类 for any 数据 that’读取或写入用户集合(其中users是集合的名称)
的 @Field
annotation defines the name of the field in the 数据base 和 地图s it to the property. If no field is annotated with @Field
then the name of the variable is taken as that of the field name. This is primarily useful when the 数据base record has different names as posed to 类 fields. e.g. the ID field, in the 类 the ID field is 用户身份
but when it comes to MongoDB the ID field name is _id
和 that’s what the @Id
field does. Alternatively you can use the @Field("_id")
instead of the @Id
和 it’ll work the same.
的 @Indexed(unique=true)
field is what sets the 用户名 field to be 独特 和 no two records can have the same 用户名. 的 reason why it’不能放在userId字段上是因为’s the ID
field 和 each DB has it’开箱即用的s ID字段是唯一的
的 UserDto
is just another 类 that represents the Data Transfer Object
返回ed 从 the service to the controller. You can look up the file in the Git存储库.
使UserDto具有几乎相同的字段的缺点是代码重复和多个对象的创建
但是如果那些不’这似乎是一个巨大的折衷,这种方法给您带来的核心优势是您拥有一层
中间的适配器将数据库字段与API响应的字段分开,这样,当数据库中的任何内容发生更改时,API用户/客户端都不会受到影响。
将UserDao和CrudRepository放在一起
弹簧如何知道要链接到哪个数据库集合的存储库?它’s all with the help of Generics. 弹簧 first resolves Crud存储库<UserDao, 串>
和 pulls out UserDao
和 looks for a 类 with that type which is annotated with
@Document
,找到后,便会从
@Document(collection=users)
现在,它已将UserRepository链接到CrudRepository,以链接到数据库中的用户集合。
服务
We’已经连接了我们的存储库和实体类,现在让’查看控制器将调用以进行交互的服务。
Below is an implementation of the 用户服务
. 的 code is 修剪med to only the relevant ones. For a complete sample
you should 抬头 the Git存储库. You can refer to the above section about the Crud存储库
methods to know
用什么方法做什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
@服务 上市 类 用户服务Impl 实施 用户服务 { 私人的 最后 用户资料库 用户Repository; 上市 用户服务Impl(用户资料库 用户Repository) { 这个.用户Repository = 用户Repository; } @覆写 上市 串 创建用户(UserDto 语境) 抛出 服务ClientException { 尝试 { UserDao 保存dUser = 用户Repository.保存(新 UserDao(语境)); 返回 保存dUser.getUserId(); } 抓住 (DuplicateKeyException e) { //处理重复的用户名 返回 空值; } } @覆写 上市 UserCountResponseDto 用户Count() { 长 用户Count = 用户Repository.计数(); 返回 新 UserCountResponseDto(用户Count); } @覆写 上市 采集<UserDto> getUsers() { 可迭代的<UserDao> 使用者 = 用户Repository.找到所有(); 如果 (!使用者.迭代器().hasNext()) 返回 空值; 采集<UserDto> 返回者 = 新 数组列表<>(); 使用者.每次(x -> 返回者.加(新 UserDto(x))); 返回 返回者; } @覆写 上市 UserDto getUsers(串 用户身份) 抛出 服务ClientException { 可选的<UserDao> 用户Optional = 用户Repository.findById(用户身份); UserDao 用户 = 用户Optional.要不然(空值); 返回 新 UserDto(用户); } @覆写 上市 采集<UserDto> getUsersByPet(串 petName) { 采集<UserDao> 使用者 = 用户Repository.findByPets(petName); 返回 使用者.流().地图(UserDto:: 新).收藏(收藏家.到清单()); } @覆写 上市 布尔值 updateUser(串 用户身份, UserDto 语境) 抛出 服务ClientException { 尝试 { UserDao 用户 = 用户Repository.findById(用户身份).要不然(空值); 用户Repository.保存(用户.UpdatedFrom(语境)); 返回 真正; } 抓住 (DuplicateKeyException e) { //处理重复的用户名 } 抓住 (例外 e) { } 返回 假; } @覆写 上市 布尔值 删除User(串 用户身份) 抛出 服务ClientException { 用户Repository.删除ById(用户身份); 返回 假; } } |
Notice the 删除 method, it directly calls the 删除ById()
和 这个 would work because the 用户身份
is the ID
field of the 数据base but what 如果 the 用户身份
is not the ID
field of the 数据base? Another way would be read the
用户,然后通过传递实例删除该记录。
1 2 3 4 5 6 7 8 9 |
@覆写 上市 布尔值 删除User(串 用户身份) 抛出 服务ClientException { UserDao 现有用户 = 用户Repository.findById(用户身份).要不然(空值); 用户Repository.删除(现有用户) 返回 假; } |
控制者
现在我们已经连接了服务,我们只需要添加控制器以实际添加可访问的接口
这样客户可以访问我们的功能’ve在这里构建(使用API)。我们赢了’涵盖基础知识
of APIs or @RestController
in 这个 blog post but you can 抬头 春季靴子休息示例
以获得更多信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
@RestController 上市 类 用户控制器 { 私人的 最后 用户服务 用户Service; 上市 用户控制器(用户服务 用户Service) { 这个.用户Service = 用户Service; } @后期制图(“ /用户”) 上市 响应实体<?> 创造NewUser(@RequestBody UserDto 语境) { 尝试 { 串 用户身份 = 用户Service.创建用户(语境); 如果 (用户身份 == 空值) 返回 响应实体.状态(HttpStatus.NOT_MODIFIED).建立(); 返回 响应实体.被创造(URI.创造("http://127.0.0.1:8080/users/" + 用户身份)).建立(); } 抓住 (服务ClientException e) { 返回 响应实体.错误的请求().标头(“信息”, e.getMessage()).建立(); } } @GetMapping(“ /用户/计数”) 上市 响应实体<?> getUsersCount() { UserCountResponseDto 用户Count = 用户Service.用户Count(); 如果 (用户Count == 空值) 返回 响应实体.无内容().建立(); 返回 响应实体.行().内容类型(媒体类型.APPLICATION_JSON).身体(用户Count); } @GetMapping(“ /用户”) 上市 响应实体<?> getAllUsers() { 采集<UserDto> 使用者 = 用户Service.getUsers(); 如果 (使用者 == 空值 || 使用者.是空的()) 返回 响应实体.无内容().建立(); 返回 响应实体.行().内容类型(媒体类型.APPLICATION_JSON).身体(使用者); } @GetMapping(“ / 使用者 / {userId}”) 上市 响应实体<?> getAllUsersById(@路径变量(“用户身份”) 串 用户身份) { 尝试 { UserDto 用户 = 用户Service.getUsers(用户身份); 如果 (用户 == 空值) 响应实体.无内容().建立(); 返回 响应实体.行().内容类型(媒体类型.APPLICATION_JSON).身体(用户); } 抓住 (服务ClientException e) { 返回 响应实体.错误的请求().标头(“信息”, e.getMessage()).建立(); } } @贴图(“ / 使用者 / {userId}”) 上市 响应实体<?> updateUser(@路径变量(“用户身份”) 串 用户身份, @RequestBody UserDto 语境) { 尝试 { 布尔值 那是成功的 = 用户Service.updateUser(用户身份, 语境); 如果 (!那是成功的) 返回 响应实体.状态(HttpStatus.NOT_MODIFIED).建立(); 返回 响应实体.行().建立(); } 抓住 (服务ClientException e) { 返回 响应实体.错误的请求().标头(“信息”, e.getMessage()).建立(); } } @删除映射(“ / 使用者 / {userId}”) 上市 响应实体<?> 删除User(@路径变量(“用户身份”) 串 用户身份) { 尝试 { 布尔值 那是成功的 = 用户Service.删除User(用户身份); 如果 (!那是成功的) 返回 响应实体.状态(HttpStatus.NOT_MODIFIED).建立(); 返回 响应实体.行().建立(); } 抓住 (服务ClientException e) { 返回 响应实体.错误的请求().标头(“信息”, e.getMessage()).建立(); } } } |
的 above is a sample snippet of the 用户控制器
for the complete code you can refer to the GitHub 资料库.
控制器必须遵守 RFC-7231 that states the standards for RESTful APIs
.You can see based on the 状态 codes 和 the 创造 用户 201 (with URL) response. During the POST
call, the server must 返回 the URL
of the 资源 that was 被创造.
应用类别
这是Application.java,它将用于运行代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
包 com.亚姆卡罗.样本.弹簧bootcrud存储库; 进口 组织.弹簧框架.开机.弹簧应用; 进口 组织.弹簧框架.开机.自动配置.弹簧Boot应用程序; 进口 组织.弹簧框架.开机.网路.小服务程序.支持.弹簧BootServletInitializer; @弹簧Boot应用程序 上市 类 应用 延伸 弹簧BootServletInitializer { 上市 静态的 虚空 主要(串[] args) { 弹簧应用.跑(应用.类, args); } } |
运行代码示例
现在我们已经准备就绪,我们只需运行代码并尝试使用API。我们’ll be using 邮差 去做这个。
We’ll first make the Post
request to 创造 the 用户:
Notice the Response Headers
has a key called Location
that is 弹簧 doing its work for obeying RFC-7231
based on the URL we 加ed in the 响应实体.created("URL")
让’现在让我们看一下数据库,对于数据库,我喜欢使用一个称为 蒙哥罗盘 而不是终端,只是使这变得容易。
的 pets 和 age are basically extra fields I 加ed up. Notice how our 用户身份
is stored as ObjectId("OurID")
和 the actual field name is _id
even though in our model 类 it was 用户身份
that’s the @Id
doing it’s job.
让’尝试立即获取用户信息:
就在那里。
也注册了其他端点,您可以类似地调用它们
终点 | 方法类型 | 描述 |
---|---|---|
/用户/数量 | 得到 | 获取注册的用户数 |
/用户 | 得到 | 获取所有注册的用户 |
/ 使用者 / {userId} | 放 | 更新用户数据 |
/ 使用者 / {userId} | 删除 | 删除用户 |
Careful about the
得到 /用户
call though imagine you have 1 million 使用者, would you want to 返回 all of them in a single call? Wouldn’对服务器来说太难了吗?为了解决这个问题,我将在另一篇博客文章中介绍分页’超出了本博客文章的范围。
结论
我们看了一下SpringBoot Crud存储库,’的方法以及与存储库交互的示例服务。
从上面的示例中删除了很多代码(例如异常处理,空检查和控制器),以带来
更着重于CrudRepository的实际方法。
请记住,CrudRepository只是一个基本的即用型功能,尽管它’s a great way to get started you may would want to define your own methods in the 用户资料库 that would be more bound to your use cases. This i超出了本博客文章的范围。 的 删除ById()
, findById()
和 保存()
they all work on the ID
field of the 数据base 和 如果 you want your own ID fields then you can 创造 them 和 write queries 和 methods in the 用户资料库 接口 和 弹簧 would wire up an implementation for you at 跑time.
源代码
那’关于Spring Boot Crud存储库的所有内容。