does these 2 ways of querying equivalent? - java

I'm a Spring backend learner, currently want to query some data from mysql. What I want is just a field from table.(I want categoryName in category).
First, I was using LambdaQueryWrapper. I wrote
categoryLambdaQueryWrapper.eq(Category::getId, categoryId);
Category category = categoryService.getOne(categoryLambdaQueryWrapper);
But category values null occasionally.
Then, I changed to simply using getById method in categoryService, everything worked fine.
Category category = categoryService.getById(categoryId);
I confused, is there anything difference between the 2 ways of querying? I thought they were equivalent before. Thank you for everyone who can help me.

The difference is that categoryService.getOne(categoryLambdaQueryWrapper) returns a single entity that matches the conditions specified in the lambda query wrapper, or null if no such entity exists.
On the other hand, categoryService.getById(categoryId) directly queries the database for the entity with the specified categoryId, returns the entity if it exists, or throws an exception if it doesn't.
So if you want to ensure that a category exists before processing it, it's better to use categoryService.getById(categoryId).

Related

Can I add condition with spring data standard interface methods

Can I add condition to provided interface method like "findAll" of spring data? For example, if the table has columns "name" and "deleted" - I can create a query asfindByNameAndDeletedIsNull which will give all names which have not been deleted. I tried "findAllAndDeletedIsNull" but this does not work - is this possible?
I know it can be achieved with #query, but was curios as how can we augment the standard methods with conditions.
You can do "findByDeletedIsNull"
See here for more info on what key words can be used in a method name.
According to the documentation, there's a specific way to name the method to have it parsed into the correct SQL. Look at this section: Spring Data JPA reference - query.
If you have the query ready, you can easily reverse-engineer which method name may help you get the same result. You can compose quite complex queries by building up from those foundation bricks. However, you cannot just create a string representation of the "where" clause as a named method.
From the document, if you want to get all records where a field is null:
IsNull | findByAgeIsNull | … where x.age is null
will mean a method name such as findByDeletedIsNull.

Hibernate loading associated entities not by primary key

I have 2 tables (really simplified for the sake of the question):
Book {
id Integer,
name String,
author_id Integer
}
Author {
id Integer,
fullName String
}
Now I need to save the Book, but I don't have author_id, I just have author's fullName.
I can get the author by name, then do book.set(thatAuthor), but I have more fields in many classes, I have to fill all those objects and it's more code than I would like for this, especially since I'm using map-struct to map everything and then I would just like to call persist and be done with it, but I can't, I have to set those fields manually.
Question: is there a possiblity to tell Hibernate to do it? I have the entity class filled with data (author's fullName is set) and it's unique so if Hibernate tried to do this he would only find 1 result, no problems there.
I have in mind such functionality: Hibernate looks at the class. It sees that some fields are set and some other are not. It takes all the fields that are set and puts them all in where clause (in my case it's only authors fullName) and tries to find a single result, throws Exception otherwise.
Not sure if you can understand me, tried my best. Don't hate me for the question, all I could find was to do what I did, loading the objects manually. If I can possibly avoid lots of code by asking this question - why not try?

JPA setting referenced property without retrieving it. Best practices

Let's assume I have Entity that have nested Entity inside it.
For example (please, ignore missing annotations, getters/setters, etc):
#Entity
class User {
private String userId;
private Set<UserOperation> userOperations;
}
#Entity
class UserOperation {
private String someString;
// This is nested referenced entity
private User user;
}
Let's assume that I want to insert new UserOperation and all that I have is userId.
Can I do something like:
// We just create new user. There is no interaction to get existing from DB. Totally only 1 insert
User user = new User();
user.setId("someId")
UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);
Or I should go that way only:
// We retrieve existing user. There is interaction to get it from DB. Totally 1 select and 1 insert
User user = em.find("someId")
UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);
What is the right way of doing it?
Because from DB perspective userOperation table just have String user reference, so ID should be enough. Java requires an object.
When call "new User" I would like to avoid, properties of existing user be flushed (as they are all not set) or JPA trying to insert new user and operation failing due to primary key violation.
Some examples are welcomed.
For your use case, there is particularly method getReference() in EntityManager. It gives you an entity object for id, but does not access DB to create it. Therefore the best solution is a slightly modified 2nd solution of yours:
// We retrieve a stub user for given id. There is no interaction with DB
User user = em.getReference("someId", User.class);
UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);
Explanation:
getReference() has the same logical meaning as find(), with the exception that it does call DB. The consequence is that it does not check if there is a row in DB table with the given id, and that the object you get does not yet contain the data. However, the object is fully capable to load additinal data when get method is called. Therefore the object is fully usable even if retrieved by getReference() - in fact it works the same way as lazy loading.
A side note to your first solution:
The first solution would not work, as it would create a new entity user and then it would fail either when storing the entity to DB if it is cascaded (persist always calls insert and it would try to insert user with the same ID as exists in DB), or it would fail that UserOperation is to be persisted while user is not. In order to fix this solution, you would need to call em.merge(user) before you call em.persist(userOperation). But again, this would call a select to DB in the same way as em.find().
The best way to do this is using the second example. We should always try to use the actual object direct from db. Working with only the db reference will be way worse to mantain.
Now speaking specifically about Hibernate, it makes even more sense to work with whole objects, especially because of Hibernate's cascade, that can and will (if cascade is set) update the child entities of the one you are persisting to database.
Well, I have to admit that always fetching objects from database may cause some performance issues especially after the database gets a huge amount of data, so it's always important to implement nice and coherent model entities, and also keep in track of database hits from your application, and try to keep it the less possible queries being generated.
As for example, your own example (the second) is clean and easy to understand, I would stick with this approach, since it's really simple.
Hope it can solve your questons :)

Wizard-generated JPA DAO method doesn't return an iterable List

I must be really stupid, but I'm at my wits' end with a JPA issue, using MyEclipse 7.5.
I am accessing a DB2 database (on an AS400) via JPA. I have reverse-engineered a simple table to provide a DAO with some precision "find" methods. So far so good.
If I run a SELECT statement over the table thus, I get 4 rows:
SELECT * FROM MyTable WHERE MyValue = '1234'
However, if I try to access these same 4 records via JPA, I get a list that's the right size (4), but which contains 4 objects which are all the same, all copies of the first object found:
List <MyTableObject> objects = dao.findByMyValue("1234");
It's almost as if the internal Query object that the DAO class creates can't iterate through the rows of data. I've tweaked the reveng.xml file myriad ways, and I've tinkered with the generated DAO, but I'm getting nowhere. Am I missing something really obvious here? I just want to get a list of objects in the same way that the conventional SELECT statement returns a resultset!
(This is MyEclipse 7.5, using Hibernate 3.2 and its associated JPA library).
UPDATE: here's the generated code that findByMyValue() passes over to (loggin / try-catch removed for clarity):
#SuppressWarnings("unchecked")
public List<PolicyStatFile> findByProperty(String propertyName, final Object value)
{
final String queryString = "select model from MyTableObject model where model." + propertyName + "= :propertyValue";
Query query = getEntityManager().createQuery(queryString);
query.setParameter("propertyValue", value);
return query.getResultList();
}
FINAL UPDATE
It was all about the model: see comments to this post. Essentially, the model generated from the reverse engineering file was invalid because I didn't have a truly unique key. Once I resolved this (spurred by comments here), all was well.
Method you've posted looks correct (although it seems rather pointless to generate this for all properties). Couple things to check:
Is MyValue property you've mentioned mapped directly on your entity (e.g. to the column on the same table; no associations are involved)?
Can you enable Hibernate SQL debug (set 'hibernate.show_sql' property to true in your configuration) and check what the generated query looks like?
Are 4 objects returned actually the same (e.g. are '==' to each other) or are they copies of each other (e.g. have the same property values)?
Can you post your mapping for the entity in question and generated SQL from #2 above?
I suspect you haven't overridden hashCode() and equals() in your JPA entity (e.g. MyTableObject). So Hibernate can't distinguish the returning rows. That a look here.

Does Hibernate's Criteria API still not support nested relations

I'd like to use Hibernate's Criteria API for precisely what everybody says is probably its most likely use case, applying complex search criteria. Problem is, the table that I want to query against is not composed entirely of primitive values, but partially from other objects, and I need to query against those object's id's.
I found this article from 2 years ago that suggests it's not possible. Here's how I tried it to no avail, there are other aspect of Hibernate where I know of where this sort of dot notation is supported within string literals to indicate object nesting.
if (!lookupBean.getCompanyInput().equals("")) {
criteria.add(Restrictions.like("company.company", lookupBean.getCompanyInput() + "%"));
}
EDIT:
Here's my correctly factored code for accomplishing what I was trying above, using the suggestion from the first answer below; note that I am even using an additional createCriteria call to order on an attribute in yet another associated object/table:
if (!lookupBean.getCompanyValue().equals("")) {
criteria.createCriteria("company").add(
Restrictions.like("company", lookupBean.getCompanyValue() + "%"));
}
List<TrailerDetail> tdList =
criteria.createCriteria("location").addOrder(Order.asc("location")).list();
Not entirely sure I follow your example, but it's certainly possible to specify filter conditions on an associated entity, simply by nesting Criteria objects to form a tree. For example, if I have an entity called Order with a many-to-one relationship to a User entity, I can find all orders for a user named Fred with a query like this:
List<Order> orders = session.createCriteria(Order.class)
.createCriteria("user")
.add(eq("name", "fred"))
.list();
If you're talking about an entity that has a relationship to itself, that should work as well. You can also replace "name" with "id" if you need to filter on the ID of an associated object.

Categories