Building Fast and Scalable Persistence Layers with Spring Data JPA

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back to the intermediate talks track if you had a chance to look at our state of spring survey 2021 results that we released yesterday you'll know that spring data spring security and spring web mvc are modules that more than three out of four spring projects have out of the community members who told us that they use spring data nearly eighty percent end up using spring data gpa and for good reasons by torben janssen let's discuss building fast and scalable persistent layers with spring data apa yeah thank you and welcome to my talk on building scalable persistence layers with spring data jpa so my name is tom janssen and i'm an independent consultant specializing in jpa hibernate and of course spring data jpa's typical java persistence stack for most applications if any one of you wants to learn more about all of these topics i invite you to visit me at thorbjanson.com where i publish new articles on these topics every week but now let's dive into today's topic and that is how to build fast and scalable persistence layers with string data gpa and but what do we actually need to do that well we need to be able to find problems as early as possible and when i say as early as possible i don't mean we recognize them when we get a support ticket or when our phone rings and the customer tells us that nothing works anymore finding them early means finding them during development or during the test but not after that and after we found them well we need a way to avoid and fix them of course and today i want to show you a few options that you have to do exactly that so when we are working with spring data jpa we need to keep in mind that there is an underlying persistence there in most spring boot projects that is hibernate and because spring data jpa sits on top of hibernate we can of course use all the features provided by hibernate but we also need to follow all the best practices and need to avoid the specific pitfalls of that persistence provider you can of course also use spring data jpa with other persistence providers like eclipse link for example and almost everything of what i show you today is also usable if you're not using hibernate as your persistence provider with one exception and that is the following part the part when i talk about identifying issues because we need to look into our persistence provider to understand what's actually going on there why performance issues arise and what we can what we can do against them and hibernate has a really useful feature for that and that is the statistics component because internally hibernate of course knows when which statements got executed and how many of them got executed and how long that took and we just need to tell hibernate that we want to know these information so that hibernate and with that also spring data jpa is no longer a black box which is one of the most common complaints when people have any complaints about spring data jpa and to get these informations we need to set the property hibernate.generate statistics to true we can do that in the application properties file and i will show you in a few seconds how you can do that but i would what i would actually recommend for real world projects is to set that as a system property on your development machines because then you are rather unlikely to ship that setting to production because collecting all these information takes some time and slows down your application and in my experience if you activate that without having any performance issues in production you will most likely have some afterwards so better don't activate it in production use it for test and development and well when we activate the statistics component we of course want to get as many information out of it as possible and because of that we should also set the log level of archiving it start to debug to get some more information and i will show you in a few seconds which information we get there but before we do that i want to talk about a second feature rather new one and that is the slow query log so we can configure a threshold above which we consider query as slow and then hibernate will log out all the statements that were slower than this configured threshold so let's get into the ide and give that a try so here you can see my application properties file and i set the hibernate.generatestatistics property to true i also set the lock level to debug and i configure these locally lock down here to lock all queries slower than 3 milliseconds so if we do that and then run this simple test case here i have a player repo which is my chess player repository which will be one of the main interfaces we will be using in in this demo here so that is a standard gpa repository nothing nothing fancy so let's run this test case we just call the find all method on that player repo which is the standard finder method provided by the jpa repository and if you take a look at the log output then we can see in a second what we get from the statistics component and the first thing we get is this block here at the end of each session we get a summary of the operations that were performed during that session so we can see here we performed 20 jdbc statements we had zero jdbc batches and then we had some cache interactions and then we performed a few no we perform one partial flush and no complete flush right because we just read a few entities the interesting part and the one that you should recognize if you would write this test case in your application is the part up here hibernate tells us that we prepared and executed 20 jdbc statements if you look at the test case again we just had a final method that doesn't look suspicious at all but anyways we executed 20 statements and now it's our job to figure out why that happened and if we scroll up a little bit i'm also logging the sql statements you can see here that we have a statement that selects a tournament for a player and another one and another one and another one right you probably already know what's going on here there are 19 of these and somewhere up here yeah that is the query that we initially executed the one that selects a chess player from the database or all chess players from the database and they are 19. so we have a typical n plus one select issue here and there we also have the output from this query log this query took four milliseconds and having it also tells us which query that was so now we know okay we're executing too many queries and we have at least one that's a little slow so we can now use this information to improve the performance of our application and that will be the stuff that we will do for the rest of this session so what are typical reasons for performance issues with hibernate where we just saw one we saw one of the most common ones the fetching of associations and there we configure the fetching behavior using the fetch type that's part of the jpa specification and part of our entity mappings the fetch type defines when a relationship will be fetched so will we fetch the tournaments when we instantiate a new chess player or do we fetch the tournaments when we access the association from the chess player to the to the chess tournaments the first one where we initialize it immediately is called fetch type eager and there we fetch the association whether or not we use it afterwards and that is the default for to run associations and lazy is the more efficient one that is also the default for the many associations and there we fetch the association when we actually use it in our code so that would be the preferred approach but i don't want to talk too long about that because i expect that you already know about eager and lazy fetching the more interesting question now is what do we do about that well what we can do is we can use query specific fetching to improve the fetching behavior of our application so let's say we stick to these recommendations and we use too many uh we use lazy fetching for too many associations and for most of our to run associations right in best case we would use it for all to one associations but let's be realistic most persistence layers or the main models don't have any fetch type configured for the 2-1 associations and switching everything from the default eagle fetching to lazy fetching is usually a lot of work and not always provides huge performance benefits so let's say we we have fetch type lazy in our domain model and we will change that in a few seconds in the demo so then we need to address another issue and that is the typical n plus one select issue caused by lazy fetching and that means that if we are if you have a code snippet like the one here at the bottom of the slide then each access to the books association excuse me each access to the books association triggers another query and if we select n authors in the first query and then iterate through them we execute these and plus one queries and queries to initialize the books association want to get the authors and that is something that causes severe performance issues if you google about that then you will find some suggestions like use fetch type eager and have any specific fetch mode and all these kind of things but please don't do that we just saw in the previous test case why fetch type eager is a bad idea there we initialized the association even though we didn't need it there are two better options for that and that are the fetch join clauses that i will show you in a few seconds and entity graphs which enable you to overwrite the lazy fetching modeled in your entity mapping so the first thing i need to do is i need to fix that part here typically typical many-to-many association between my chess player and my chess tournament and you should of course never set fetch type eager in a real application so when i do that and just run this test case let's take a quick look at the method here the find with tournaments by method just is a derived query it just selects all chess players from the database and if we execute execute this test case where we get all chess players and then iterate through them and print out the name and the number of tournaments then we can see in our lock output that we again have 20 queries and we have one select statement that gets the tournament for each player directly before we print out the corresponding log message so probably familiar what's going on here this call of the get tournaments dot size method triggers another query to get the tournaments association here and that is what we actually want to avoid because well first of all it only works in transactional context and right you saw the second reason it's causing a lot of queries so let's use the fetch join clause or joint fetch clause that i just mentioned on the on the previous slide so you can use it with a query annotation so that we can define our own query statement for this specific repository method what do i do well i select chess players and then i do a left joint fetch on p dot tournaments if we take a look at the chess play entity we can see that tournaments is our mapped many-to-many association so by doing a left joint fetch i tell hibernate or any other jpa implementation to initialize the tournaments association on each and every chess player that we get from the database and you will see a huge difference in the log output and in most applications also a huge performance improvement so let's see here now we have just one jdbc statement and you can see here that we printed out a lot of log messages with famous chess players and the number of tournaments they have in our small test database so how does that work you can see the query here so we have our chess player and then we have two left other joints from the chess player to the association table because it's a many-to-many association and then to the chess tournament table and well that's expected right that would happen with an usual join clause as well the special part about the joint fetch clause is that it also extends the association table the the the projection it selects the records from the association table and all columns from the associated chess tournament you can see that here with the chess turn two alias where we select the end date the name start date version and some will here also the id and well your jpm limitation uses that to instantiate here here a list of tournaments and fill it with chess tournament entities fully initialized managed entity objects just in the same way as if you would have just called the getter on a lazy association and hibernate would have fetched it with a separate query yeah if you have any questions feel free to post them in in the stack channel see there are already a few people um post them there i will take a look at that maybe during the talk as far as possible or afterwards in the q a part so let's get back to the chess player repository um we already improved the use case using the or the test case using the left join fetch clause from the chess player to the tournament and there's a second option and that is an entity graph so normally using plain jpa we would need to define a named entitygraph like this annotation based give that thing a name and define attribute nodes and these attribute nodes specify which entity attributes on the entity that we select we want to initialize and in this case it's of course tournaments so what i could do now is i could reference this named entitygraph by name in my repository and set it as the value attribute but there's actually an easier way and that is using the attribute paths attribute where we can just put in the name of the attribute we want to initialize in this case tournaments so let's get back to the test case and run the test case again take a look at the log output then we can see that we again just used one jdbc statement we have here all of the log output and here we have the same query statement as we had before so we join from the chess player to the association table and from there to the tournament and here we select all information from the chess player and all information from the tournament so by doing that again we have all the information that we need one thing that you might have recognized is now we have a slow query statement here because our query is of course getting more complex than it was without fetching the hs tournament and that's the case for joint fetch clauses and for entity graphs as you see in the sql statement they create the same statement that's sent to the database so one thing you always need to keep in mind here is your query gets more complex you're selecting more data you're selecting not only more more columns from the database you're also creating a product with each join so now we get each chess player as often as she or he has played in a tournament and because of that our reside set grows rather quickly and that is always something you need to keep in mind because you can of course now use multiple joint fetch clauses in this query or you could create more complex entity graphs and as long as you model your too many associations as java youtube said hibernate generates you all kinds of curves even one with seven eight or ten join fetches and i've seen that in multiple projects and that will not solve your performance issue it will replace your n plus one select issue with a new problem because now the query will be really slow because you're selecting thousands of database records millions of data points and if your dba looks at that query they will probably just shrug and say well that query is that big and complex i can't do anything about it and that's because we are selecting the wrong stuff so please be careful about using complex entity graphs please be careful about using multiple left join fetch clauses there are some people who say you shouldn't join more than one association i don't see it that strictly because there's a difference between joining an association that contains one two or three records compared to one that contains a few thousand but the general idea is correct you should always take care that you don't join two complex associations and not too many of them okay that's about association fetching at least for now um the next thing i want to show you is a small pitfall that is hibernate specific and that is the handing of many too many associations so you probably know you can model it too many association as list set a map you have quite a few options there and from a java point of view you would probably prefer a list as a simple data structure for too many association because at some point all of us learned lists are a little more efficient than sets because we don't check for duplicates and all these kind of things well you can forget about all of that if you are using hibernate as your persistence provider with spring data gpa because hibernate handles these lists rather inefficiently for many too many associations when you perform right operations on them and that is something we will take a look at now so here we have a chess tournament and uh get just one chess tournament from the database and i decide okay player with ide 34 shouldn't be part of that tournament maybe they signed up in the beginning and then for some reason they they can't attend so i decide to remove that player from the list of players that participate in that tournament what we would normally now expect is that hibernate executes a query that removes the one record from the association table that matches the chess player with each s tournament but if you look here there you can see that hibernate is obviously doing more than that there are 18 jdbc statements and 18 is a lot so what's going on here we already see it here we have an insert statement on the chess tournament players table but we are not adding anything to the association we have another one and another one and another one so you might already have an idea what's going on here but if we scroll up you can see that we have a bunch of insert statements here and here is the delete statement and that was what we expected we were expected to see a delete statement but not this one because here we are removing all the records from the chess tournament players table with the tournaments id that is equal a provided value so right now we are removing all players from the tournament even though we just wanted to remove one of them and then hibernate re-inserts each and every one of them who's still in the list and as you can see here that is rather inefficient on an already small list of players it gets worse if you do that on associations with more elements that would be a tournament with 256 players for example if you would re-in their 255 records and there's an easy way to solve that and that is my tournament um yes my chest tournament entity there's an easy way to solve that and that is we switch the data type from list to set when i do that up here i of course also need to switch the data type down here oops um like this okay so now we work with a set instead of list let's see how that changes the whole database interactions okay so it's much better than before instead of 18 statements we are now executing four um we have the delete statement that we expected so we now remove that one specific record from the association table and nothing else and that's more than good enough and then we of course have a select statement to get the chess tournament and the chess player caused by these two get by id calls and then hibernate needs to internally update the chess tournament table as well when we're removing the record but that's an hibernate specific thing that unfortunately we can change but what we can avoid is these additional 14 statements that we added 14 players to the tournament even though we just wanted to remove one of them okay so that's all about association management for now we had a look at the fetch types and said okay we want to use lazy fetching for all of our associations but when we do that we also need to make sure that we fetch all the records that we need for our current use case as efficiently as possible and avoid the n plus one select issues that we just saw so there we wanted to use trifetch clauses or entity graphs to tell hibernate or any other jpa implementation that's used by springdata jpa to fetch the required records from the database and then we also need to make sure that we use the right data type to avoid any inefficiencies and right with the stranded spring boot project when you add spring data jpa you usually get hibernate as your persistence provider and that handles these lists for many-to-many associations rather inefficiently so please use a set and ignore the theoretically performance slower performance of java util sets compared to java utilis because they you you will not recognize them until you add several thousands of probably a few million elements into the um into the set and well if you fetch a few million entities from the end from the database the performance of your list or set will not be the thing that makes your application totally unusable it will be the database query the network traffic and all the other things okay so i mentioned earlier that if we join fetch a lot of associations then we are usually selecting the wrong data we are selecting way too much and that is of course always one angle from which we can tackle performance issues uh we should only perform the operations that we need and that includes we should only select the data that we actually need for our project or for a use case and we should select it in the way that the most efficient for that use case and we can choose between three different projections with spring data jpa we can select entities which are great for write operations because they are managed by the current persistence context and that means when we call a setup method on an entity object then the um change will be committed or will be updated in the database before the transaction gets committed we can be sure that we just have one entity instance for each database record within one transaction and all these kinds of things so all the benefits that we have for write operations are based on entities their life cycle model and the functionality provided by the persistence context and that makes well entities the best option for write operations of course but what about read operations right for if we just read the data then we don't need a lifecycle management we don't need someone to check if we changed any attribute value to then generate um sql update statements because we know we just read them to i don't know present them in the front end or do some calculations but not to change anything and in all these cases we should try to keep the overhead of this management functionality as low as possible and there are two options for that that are scalar values and dtos and both of them are right in based on tests that are run on my my local machine at about 20 faster than entities um if you select exactly the same data and run database and everything on on a local system so just the management overhead slows down the instantiation and management of the object that's performed by your persistence provider so the question then is well if scalar values and dtos are better and faster for read-only operations which ones should you actually use well i would say you should not use scala values at all because a scalar value projection is an object array and then you need to iterate through that array and remember okay i selected the first name in the first column and the last name in the second and then the date of birth and then you need to access these elements by their index and extract them from there and let's be honest nobody wants to do that it's it's annoying it's hard to maintain and compared to dtos it provides almost no performance benefit and that's why dto projections are my recommended projection for all read operations and if you're just using plain jpa then you would only be able to use the dto classes so there and we will do that in a few seconds there you need to implement a class with constructor and data and setter methods and everything and well your persistence provider could instantiate objects of that class for your crew website spring data jpa makes that a little easier you just need to define an interface and spring data jpa will then generate a class based on that interface definition so let's give that a try so here we have a simple query that derives query and selects player name object based on a first name so what is player name player name is my dto class so that is what you could also do with plain jpa or hibernate there you would write jpqa query with a constructor expression luckily we don't need to do any of that so here you can see just a basic java class no entity annotation so it's not managed that's why it's faster so first name last name attribute a constructor that sets all attributes and if you get answer methods so if we now go to let me close a few things here so if we then go to the test case and run this test then we will see an important difference compared to all the queries that we had before in all the previous test cases we worked with entity objects and that meant that our persistence provider had to select all columns mapped by that entity class and then instantiate a minute object and set all attributes in this case we only wanted to get the first and last name of the chess player in this case of the current world champion so a query statement that just selects first and last name from the database would be more than good enough and we get exactly that if we are using dto projections you can see the generated query here so that is the same thing you would write in plain jpa or hibernate with a constructor expression that describes a constructor called the player name class with first name and last and then we see here okay we've got a player name object and there's of course the information in there but there's an easier way to do that with spring data jpa as i promised you earlier you can use an interface and just define the interface with a few getter methods and as long as the getter method methods match the name of getter methods on the in this case chess player entity spring data jpa will generate a query for this method definition derived query and instantiate the player name in objects for you so i already executed the test before we switch to the repository and you can see that here my player name and is now one of spring data jpa's lovely named internal classes you can see that there's the first and last name of our chess player and if we scroll up here we can see that we executed exactly the same sql statement as before so we select just the first limit last name from the chess player table and the only difference here is that is now handled by spring data gpa the instantiation of these objects and not by the constructor expression but that is a detail that doesn't have any impact on the performance at least not in this case so that makes things a lot easier when i'm talking about these dto projection i also get always asked about how to use them with native queries because native queries are great as soon as your query gets complex as soon as you have to extract data in a complex way for some reporting use cases or where you need to transform data and stuff like that so all the things that you usually can't do with a derived query and also not with the jpqa cream and well i tried that here so in in theory the alias from the query could get mapped on the attribute name of the either dto class or the interface so let's try to do that with the dto version of it so where we get player name object and you can already see it here my test case expect that a converter not found exception gets thrown by spring and where is it here it is here you can see that we now got a converter not found exception look where the front capable of converting that is the same lovely class name that we had before to player name so now there are various ways to fix that one would be to define an sql result set mapping which is an annotation based mapping definition defined by the jpa specification to map native query result to any kind of data structures entities dtos scalar values whatever you need and then you find a named named native query and when you define the named native query then also um reference the sql result set mapping there as well but that's more complicated than it has to be um what you should do instead is if you want to work with native queries and want to use a dto projection then please use an interface like this one right the one that we used before because if you do that then spring data jpa handles all of that for you so you can see i call my native by first name repository method which is this one up here same sql statement as we executed before the only difference between these two methods is the return type the interface in this case and this time right we got the name of the chess player here you can see that everything worked as expected we have our native sql statement here which is exactly the same as i defined as the query and spring data jpa was easily able to map that to the generated object that implements the interface but that's not all of the things that you can do with spring data jpa and dto projections um the spring data gpa team took it one step further you can also use nested associations and spring expression language on your interface based dto projections but please don't do that for performance critical use cases it looks great as a feature it technically works fine unfortunately you use the you lose the benefits of using a dto protection so let's take a look at an example so here i have my tournament interface and well that's a rather handy dto projection where i have the name of the tournament and a list of player name interfaces for all players who participate in that tournament so that's great to present the tournament with all the players on a website for example and what spring data jpa will do is it will use the get name getter on my tournament entity to get the name of the tournament called the get players get her on the tournament to get the together all players of the tournament and for these chess players spring data jpa will use the player name interface or rather the object that implements that interface the class that implements that interface as a projection so let's run this test case and see what happens so we would print out the number of players who participated in the tournament and the name of the tournament in a log message so first of all we see here that we executed two jdbc statements which is probably more than we expected um considering that we had one query that was supposed to get all the information right so the next surprise is up here we not only selected the name of the chess tournament we selected the entire table we selected all columns mapped by the chess tournament entity class and then we see this output here which is that lock statement so before we print out any information or any information from the tournament we see here that we got another statement that selects the players of that tournament and then we have the the lock statement so based on this chess tournament no tournament in projection we selected all chess tournament entities and we selected all chess player entities that are associated to that tournament and we lost all the benefits of the dto projection we now have a bunch of managed entities in our current persistence context we selected way too much information and all of that just because we have this get players thing here in the in our tournament interface something similar happens if we do use the expression language so here you can see my player full name interface and there i use the expression language to concatenate the last name comma and the first name so i just want one get a method that i call to get the full name of the player because that's just a little more handy than concatenating that myself so if i now run the test case there it is um then you can see here that we the test case passed that's at least something we printed out the name of the player and we just use one statement so not too bad but what did we actually select well we again selected the entire chess player entity even though we just defined our player full name interface which just referenced the last name and first name attributes in this expression language where the reason for that is we use the expression language the only thing spring data jpa can do is get the entity and then apply the expression language on top of that unfortunately that gets us to the point where using a dto projection doesn't make any sense i mean we now define an additional interface to perform the same work as if we would select an entity class but actually get less because we don't have access to any of the other attributes of the entity and can't use it to perform right operations there's a better way to do that i defined my better player full name interface and that doesn't use expression language as you can see that one uses two getter methods that match the getter methods on the chess play entity and i have a default method get full name and that one does in the java code the same stuff that i did previously in my expression language and if i now run that test case then you can see that the result is the same and we only selected the two columns that we actually need so now we again have the full benefit of our dto projection we didn't select any entities that means we didn't select all the columns mapped by the entity and we didn't instantiate manage objects which actually means with hybrid reinstancing instantiating two objects for each entity so that it can perform dirty checks because we didn't do that we have no flash interactions down here because right there are no entities that hibernate could flash and all of that gives us way better performance than using the expression language as in the example before okay that's all about projections for now so when you're working with spring data jpa please keep in mind there's a database in the back end where queries get executed and you should only select the information that you need and you should select them in a way that's ideal for your specific use case and that means for write operations you should use entity classes because they support the entity lifecycle model they support the automatic generation of sqa statements and all these useful things and for read operations please use dto projections there you don't have the management overhead of entities and you can fine-tune your projection to the data that you actually need and that means you select less data from the database and you store less data in local memory when you do that please be aware that dto interfaces are the easier approach with string data jpa but that you shouldn't use these advanced dto projections like nessus associations and spring expression language okay let's come let's talk about the first last part of this talk caching which is the go-to method for a lot of development teams when they are thinking about performance problems because we have a lot of data that we read over and over and over again in our application and right wouldn't it be just better if we select it less often if we have if we store it in local memory the answer to that is it depends as soft so let's take a look at this what i'm showing you now is caching as it's defined by the jpa specification and implemented by hibernate on top of that you have application caching so there you can use other approaches to cache stuff within your application but not the persistence layer but that's a different topic so if we are talking about caching in jpa then it gets a little tricky so most developers think okay we have somewhere hibernate session and that somehow interacts with database that's not entirely wrong but it's only a small part of what's actually going on so within each session we have a first level cache that one contains all the entity objects that we persisted within the current uh transaction or read from the database so that ensures that you don't trigger a database statement every time you call a setup method on an entity object and that ensures that you only have one object representation of each record in the database within each session but as you can see it's pretty hard to get any real performance benefits in a multi-user system out of that because each user has its own session and we don't get any benefits if someone has read that data we get that with the second level cache that one contains entity objects and then we have a hibernate-specific cache the query cache which technically is part of the second level cache but it's so different that it's easier to present it as a second session independent cache that one contains query results now we will not have enough time to get into the details of the query cache but let's focus on the second level cache it's the one that's mo used more often anyways so the second level cache contains just entity objects and gets used whenever you find an entity by its primary key so when internally the entitymanager find method is called or if you traverse association so if you call the getter method on a managed association well under the condition that the information is available to that but more about that in a few seconds so you saw that in the in the last diagram this cache is session independent and that's why it's so useful in multi-user applications because there you can benefit from the situation that someone has read the same data just a few seconds ago but that also creates some additional problems because now you have an external cache that you need to keep in sync and that you need to check before you execute a query and all of that takes a little bit of time creates a small overhead and that means you need to have enough hits on the cache to compensate for all the overhead and a rule of thumb when using hibernate is about 9 to ten successful read operations for each rider bridge you need to activate the cache in your application properties i will show you that in a second and you do that by choosing the shared cache mode you have the choice between all non-enable selective disable selected and unspecified i recommend using enable selective using disable selective is also fine enable selective means you need to annotate your entity class if you want to cache it in that case you hopefully think before you add an entity to the cache and you are sure that you have the nine to ten read operations per white operation this every selective is the opposite that's cached by default and you need to deactivate it which also works but to me always feels a little risky okay what do we need to do now um i have a player controller that's just a simple rust controller um and that's uh where did we left off um we had a quick look at the chess player controller where we just get an entity by its primary key and we have this test case where we call the player controller twice so what do we need need to do to activate the cache so that now these two codes of the get player id don't trigger two database statements where first of all i activate the shared cache mode and set it to enable selective and now we need to take a look at our entity classes we have the chess player they have a cachable annotation at the top which is part of the jpa specification and just tells my persistence provider to cache this entity and i do the same for my chess tournament and now comes an important tip you should also do that for your many too many associations and for all associations on entities that don't map the foreign key column because in that cases hibernate doesn't cache the association between these two objects let me quickly start the test case and then i keep explaining so this is important because otherwise you would cash the chess player and the chess tournament in your cash but you wouldn't cache the association between the two and that would mean that when you call the getter for the tournaments attribute hibernate would trigger a new database query to because it doesn't know which tournaments belong to that player and that's something that always causes confusion so what do we see here we have our first iteration there we select the chess player from the database we put it in the cache and the second one doesn't need a jdbc statement we just got the object from the cache now unfortunately now i see my time is almost up so don't worry we covered all the important parts and i hope to see you in the q a call in a few seconds and if you want to follow me and all the spring data content that i put out then please visit tombjensen.com hope to see you in a few seconds in the q a chord thank you thurgon that was awesome as with every session turbine's headed over to the post session zoom now as we speak for your questions and your comments you'll find a link to that zoom in the slack channel for this talk and the slack q a will continue for some more time today so make sure you make the most of it
Info
Channel: SpringDeveloper
Views: 1,472
Rating: 4.8666668 out of 5
Keywords: Agile
Id: smyFi4OCHDE
Channel Id: undefined
Length: 54min 51sec (3291 seconds)
Published: Wed Sep 22 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.