What are the arguments while extending JpaRepository<T, ID> - spring

I am trying to connect a DB in a SpringBoot Project, where i created a repository file for the access. When i try to extend the repository with JpaRepository, i find that it requires two arguments which are defined as below:
public interface UserRepository extends JpaRepository<T, ID> {
}
In my use case the Template is UserEntity which has a String as primary id. So, my doubt is, what is the ID that is mentioned in the above JpaRepository<T, ID> . Almost everywhere i see, it is used as Long. So if i understand it right the ID is the datatype of the primary Key (in my case String) or is it something independent of it and can be always used Long ??
Note: I'm not at all well versed in Spring!

Yes,
ID should be the same type as primary key of your entities (which are of type T). IT became clear after you take a look at declared methods in JpaRepository interface, for example:
List<T> findAllById(Iterable<ID> ids);
You see Long as primary key almost everywhere only because its the most popular primary key type (and works best for autoincremented and/or surrogate primary keys).
In your case, if you have #Id field in your entity of type String, then ID parameter should be String

Related

Mongo Spring #Document annotation

I'm trying to work with MongoTemplate and Spring and while looking on some other peoples code I was that everyone uses #Document annotation.
I did not used it at all up until not and everything worked fine for me.
I'm afraid I'm missing something and could not find any specific detailed information about the benefits of #Document annotation.
#Document is an annotation provided by Spring data project.
It is used to identify a domain object, which is persisted to MongoDB.
So you can use it to map a Java class into a collection inside MongoDB.
If you don't use Spring Data, you don't need this annotation.
I wrote a German blog post about how to use Spring Data for MongoDB - #Document is used there also:
https://agile-coding.blogspot.com/2020/10/keine-ahnung-von-mongodb-dann-nimm.html
This annotation serves only for specifying collection properties.
You can basically create simple class without any annotation needed.
Document annotation serves you for example when you are not happy with autogenerated collection name. If you do not specify #Document annotation, you still have the same result, for example
[
{
"_id": {"$oid": "62b43525de57ec7dec41a286"},
"_class": "com.example.demo.Person",
"name": "Adam"
}
]
This annotation may however be important for some kind of annotation based process to target which document are in use (maybe for indexing and so on)
Same goes for Id annotation:
if you do not provide any id specification, Id field is automatically added by mongo
if you have field named as "id" of type BigInteger, ObjectId or String, then this field is automatically populated after insert
From the docs https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo-template.id-handling
11.5.1. How the _id Field is Handled in the Mapping Layer
MongoDB requires that you have an _id field for all documents. If you
do not provide one, the driver assigns an ObjectId with a generated
value. When you use the MappingMongoConverter, certain rules govern
how properties from the Java class are mapped to this _id field:
A property or field annotated with #Id (org.springframework.data.annotation.Id) maps to the _id field.
A property or field without an annotation but named id maps to the _id field.
The following outlines what type conversion, if any, is done on the
property mapped to the _id document field when using the
MappingMongoConverter (the default for MongoTemplate).
If possible, an id property or field declared as a String in the Java class is converted to and stored as an ObjectId by using a Spring
Converter<String, ObjectId>. Valid conversion rules are delegated to
the MongoDB Java driver. If it cannot be converted to an ObjectId,
then the value is stored as a string in the database.
An id property or field declared as BigInteger in the Java class is converted to and stored as an ObjectId by using a Spring
Converter<BigInteger, ObjectId>.
If no field or property specified in the previous sets of rules is
present in the Java class, an implicit _id file is generated by the
driver but not mapped to a property or field of the Java class.
When querying and updating, MongoTemplate uses the converter that
corresponds to the preceding rules for saving documents so that field
names and types used in your queries can match what is in your domain
classes.
Some environments require a customized approach to map Id values such
as data stored in MongoDB that did not run through the Spring Data
mapping layer. Documents can contain _id values that can be
represented either as ObjectId or as String. Reading documents from
the store back to the domain type works just fine. Querying for
documents via their id can be cumbersome due to the implicit ObjectId
conversion. Therefore documents cannot be retrieved that way. For
those cases #MongoId provides more control over the actual id mapping
attempts. Example 62. #MongoId mapping
public class PlainStringId { #MongoId String id; }
public class PlainObjectId { #MongoId ObjectId id; }
public class StringToObjectId { #MongoId(FieldType.OBJECT_ID) String id; }
The id is treated as String without further conversion. The id is
treated as ObjectId. The id is treated as ObjectId if the given
String is a valid ObjectId hex, otherwise as String. Corresponds to
#Id usage.

how should i get data from mongo db using spring mongo repository?

i am using mongodb in my spring boot application and i am using mongo repository interface to get data from the database this is the way i am getting the data .
School sch=repository.findOne("id");
this will give me the school object then i can get all the data from there but my question is will it affect my application's performance if i get the whole object everytime i need some data from that object even if i need some fields . if so what will be the method to do that i searched and i see that using Query annotiation i can limit the fields but even then it give whole object it put all the other fields null and data is only at fields which i specify . any guidence will be helpfull .
You can use a projection interface to retrieve a subset of attributes.
Spring Data Mongo DB
interface NamesOnly {
String getName();
}
interface SchoolRepository extends Repository<School, UUID> {
NamesOnly findOneById(String id);
}
So after reading the documentation and reading other options this is the only solution i find where i can search the result using id and get only field i want in the result since i am only searching using the id so i have to overload the findbyId and since projection interface only change the return type i can not use that so here is what i did .
#Query(value="{_id:?0}",fields="{?1:1,?2:1}")
List<School> findById(String schoolId, String fieldOne,String fieldTwo);
here ?0 is a place holder for schoolId and ?1 and ?2 are the placeholder for the fields name so now i can create these overloaded method which i can use to any no of fields the output is list of school since i am using id which is primary key so its just one school object list so i can just do get(0) to get my school object and it will have all the other fields as null this is the best i could find please share your thought to improve it i would love to hear other solutions .

is it possible to have conditional #Transient field?

Let's say if I have an Entity named person with lots of information including SSN. When other user query this person, I want to show a 'lite' version of person Entity. I could've done so by annotating SSN with #Transient, but that means the person himself would not get this field too. Is it possible to reuse the same Entity but return two different json to client? I'm using spring boot.
First of all #Transient just means that the value, the SSN in your case, won't be persisted to the database.
As for your problem annotations are static and cannot be applied dynamically.
You have 2 Options:
Define a new View class for your user.
Look at JacksonJsonViews

How to use #SecondaryTable with CrudRepository?

I'm using SecondaryTable to map bean schema to multiple tables:
#Entity
#Table(name = "address1")
#SecondaryTables({
#SecondaryTable(name="address2")
})
How an I then tell spring to create a Repository that uses the values from table address2?
interface AddressRepository extends CrudRepository<Address, Long> {
//this one uses address1
}
I then want to be able to query only the secondary table by the CrudRepository. But how can I explicit tell spring to query the address2 table using the AddressRepository above?
(think of address2-table as a kind of archive table. Only if the address is not found in the primary address1-table, then address2 should be queried).
You can represent two tables which are store different but related data in one entity by using SecondaryTable like user and user_address tables. What You are trying is storing same data in two different tables whhich are identically same. Therefore, SecondaryTable doesn't fit your needs. You should use inheritance annotation with strategy table per class. Here is an example;
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class AddressBase {
...
...
...
}
#Entity
#Table(name='address1')
public class Address1 extends AddressBase {}
#Entity
#Table(name='address2')
public class Address2 extends AddressBase {}
Then, you can write repository for each entity and do whatever you want.
I think you are misunderstanding what #SecondaryTable is for. It is a way to split a single entity among different database tables.
From the docs (http://docs.oracle.com/javaee/7/api/javax/persistence/SecondaryTable.html)
Specifies a secondary table for the annotated entity class. Specifying
one or more secondary tables indicates that the data for the entity
class is stored across multiple tables. If no SecondaryTable
annotation is specified, it is assumed that all persistent fields or
properties of the entity are mapped to the primary table. If no
primary key join columns are specified, the join columns are assumed
to reference the primary key columns of the primary table, and have
the same names and types as the referenced primary key columns of the
primary table.
Your annotations, in their current state, say that there exists an Address entity, with data in the address1 and address2 table, which can be combined by joining on the primary key of the address1 table.
I think for what you want to do, and have it work without overriding your CrudRepository, is to just use different tables. You will need to query both tables anyway if I am understanding you correctly.

What's a good way to handle Generic Foreign Keys in JPA?

We have an Spring 3.2, Hibernate 4.2 application.
Our application has an upload module, where you can basically upload all kinds of files. A file upload generates also a database entry:
id, directory, filename, mimeType, userId
So basically it should be possible to upload a file from everywhere in the application. And we don't want a new UploadEntity for every possible Entity. So we thought about using some kind of generic table for uploads:
id, directory, filename, mimeType, userId, FOREIGN_KEY
The problem of course is, that we can't set a concrete data type for this foreign key in JPA, because it can point to Entity A or B or C or ... We use UUID as keys in our application, so we thought about two solutions:
Make the foreignKey simply of type UUID and always save the foreign entity's id.
Make the foreignKey of type Object with a #ManyToOne annotation, but of course we can't provide a target entity for that.
But maybe there's some other much better/easier solution for this. What do you think?
I forgot to tell you, that each entity we use implements Persistable<UUID>, so is it possible to use this interface as type for the foreign key?
Btw: We need never to use the reference from UploadedItem -> SomeEntity. We only need the other way: SomeEntity -> UploadedItem.

Resources