Where Spring has always come in incredibly handy is by simplifying the things we do with Java and taking some of the guesswork out of these common operations. A lot of us in the Java community have taken it a step further by adopting Groovy (or another JVM language) as our main coding mechanism. The deal gets even more sweet when you mix in Spring's dependency injection capabilities as well as its myriad framework features.
Let's face it: if you're an engineer building "business" apps, most of what you're going to do is take data from one place and put it into another. There are a lot of creative ways to do this. I'll admit that I've occasionally gone overly elaborate when it wasn't necessary, but let's face it: boredom and repetition force us to some strange things sometimes. What I've seen with Spring Boot so far is really changing the entire discourse as it concerns these mundane activities. It frees us up to do things that are less repetitive. And potentially more creative!
Step 0: Bootstrapping
Using the build automation tool of your choice (I like Gradle), follow the guidelines on Spring's site to make a simple build script. Also, create a simple bean annotated with @Configuration and @ComponentScan
Step 1: Persistence
We usually start with some sort of a data model. This has been exceedingly easy with Spring. You have some tables that you need to represent as objects in your code? No problem.
import javax.persistence.*
@Table(name = "item")
class Item {
@Id
@Column(name = "item_id")
Long id
@Column(name = "item_name")
String name
}
import javax.persistence.*
@Table(name = "item")
class Item {
@Id
@Column(name = "item_id")
Long id
@Column(name = "item_name")
String name
}
Step 2: Make a repository
This is where it starts to get pretty cool. The
import org.springframework.data.repository.*
interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
Item save(Item item)
Item findOne(Long id)
Page<Item> findAll(Pageable options)
}
You see we used the souped-up PagingAndSortingRepository, but there are other options that are slightly more generic. It all depends upon what you need. At this point, you could make a rudimentary Spring app that accessed your database, just by wiring in your repo.
.
.
.
@Autowired
ItemRepository repo
void foo() {
repo.save(new Item(id: 1L, name: 'foobar'))
assert repo.findOne(1L).name == 'foobar'
}
.
.
.
@Repository
stereotype has really evolved over time. To make a fully functional repository that can be wired in for data access, just make an interface that looks something like this: import org.springframework.data.repository.*
interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
Item save(Item item)
Item findOne(Long id)
Page<Item> findAll(Pageable options)
}
You see we used the souped-up PagingAndSortingRepository, but there are other options that are slightly more generic. It all depends upon what you need. At this point, you could make a rudimentary Spring app that accessed your database, just by wiring in your repo.
.
.
.
@Autowired
ItemRepository repo
void foo() {
repo.save(new Item(id: 1L, name: 'foobar'))
assert repo.findOne(1L).name == 'foobar'
}
.
.
.
Step 3: Now it gets really cool
This is all fine and dandy, but not super practical. What we'd normally do at this point is whip up some kind of a ReST service that would broker the situation for us. The fine folks behind Spring realize that we're sick of doing this repeatedly, and they've made it insanely easy. Enter Spring Data REST. We can modify what we have above incredibly easily. If you're using Gradle, you just have to make sure you have the right dependencies:
compile("org.springframework.boot:spring-boot-starter-data-rest")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
Then add another annotation to your config:
@Import(RepositoryRestMvcConfiguration)
Then make a few minor alterations to your repo:
import org.springframework.data.repository.*
import org.springframework.data.rest.*
@RepositoryRestResource(path = "item")
interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
Item save(@Param("item") Item item)
Item findOne(Long id)
Page<Item> findAll(Pageable options)
}
Start up your application, and you'll have an endpoint hanging off of your context root at "item." If you access it, you'll see that you get a JSON response. What's pretty nifty here is that it will only support the ReST equivalent of the operations you've called out. See the Spring site for a full list. Given the code we've sketched out, you can do a GET, one with an ID parameter and one without, and a POST. If you POST directly to the "item" endpoint, you'll see some data saved. Your body would look something like this:
{
"id": 2,
"name": "foobar"
}
This is really what makes this framework cool. For a large percentage of the apps, there's simply no need to even write a services layer. Model up your objects, spell out your data operations in an interface, and you're set. No more boilerplate madness.
Things to consider
In closing, if you're thinking about making the data situation a bit more robust, bear in mind that Spring Data REST deals in "links" when modeling relationships. In referring to an object, you have to provide its URL. It appears to me that Spring just peels the meaningful part off of the URL you provide, but I haven't dug deep enough to guarantee it.
A final thing that I hope can save some of you a bit of time: you can't persist child objects through the parent object's REST endpoint. Say "Item" has "ChildItem" objects. You will need to expose a ChildItem repository with its respective @RepositoryRestResource. This may seem unwieldy at first, but I think it's fair to have to call out the ReST operations you'd like the child object to support. Say you want to create a child object:
{
"id": 4,
"parent": "http://my.site.com/item/2",
"foo": "bar"
}
This was another thing that had me running all over the place and didn't seem to be well-documented.
Happy Springing!
No comments:
Post a Comment