Spring JPA mongodb using annotations - spring

Help me understand this. I am using spring-data-mongodb without hibernate or any other jpa provider. My domain model is like this:
public class User {
#Id
private String id;
private String username;
private String password;
...
}
I run a test class to populate a few users in my mongodb, which works fine. But if I add a few more annotations like this:
public class User {
#Id
private String id;
#Field(value="uname") private String username;
#Field(value="pass")private String password;
...
}
my test class adds just one user, the next one throws exception complaining of duplicate entries -
org.springframework.dao.DuplicateKeyException: E11000 duplicate key error index: gldata.user.$username_-1 dup key: { : null }; nested exception is com.mongodb.MongoException$DuplicateKey: E11000 duplicate key error index: gldata.user.$username_-1 dup key: { : null }
What am I missing here?

Although I didn't find out why this was happening, I found a workaround. Using ObjectId instead of String for id field, works very well.

Related

Spring Boot + Webflux + Reactive MongoDB - get document by property Id

I'd like to find all Offer documents by Offer.ProductProperties.brand:
#Document(collection = "offers")
public class Offer {
#Id
private String id;
#NotNull
#DBRef
private ProductProperties properties;
ProductProperties:
#Document(collection = "product_properties")
public class ProductProperties {
#Id
private String id;
#NotNull
#NotEmpty
private String brand;
Service:
Flux<ProductProperties> all = productPropertiesRepository.findAllByBrand(brand);
List<String> productPropIds = all.toStream()
.map(ProductProperties::getId)
.collect(Collectors.toList());
Flux<Offer> byProperties = offerRepository.findAllByProperties_Id(productPropIds);
But unfortunately byProperties is empty. Why?
My repository:
public interface OfferRepository extends ReactiveMongoRepository<Offer, String> {
Flux<Offer> findAllByProperties_Id(List<String> productPropertiesIds);
}
How to find all Offers by ProductProperties.brand?
Thanks!
After reading some documentation found out that You cannot query with #DBRef. Hence the message
Invalid path reference properties.brand! Associations can only be
pointed to directly or via their id property
If you remove DBRef from the field, you should be able to query by findAllByProperties_BrandAndProperties_Capacity.
So the only ways is how you are doing. i.e. Fetch id's and query by id.
As I said in the comment, the reason it is not working is because return type of findAllByProperties_Id is a Flux. So unless u execute a terminal operation, you wont have any result. Try
byProperties.collectList().block()

Spring Data Rest Does Not Update Default Value in DB

I have a Spring Boot application using Spring Data REST. I have a domain entity called User with a boolean field isTeacher. This field has been already setup by our DBA in the User table with type bit and a default value of 1:
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // This Id has been setup as auto generated in DB
#Column(name = "IS_TEACHER")
private boolean isTeacher;
}
And the User repository:
public interface UserRepository extends CrudRepository<User, Long>{
}
I was able to add a new user by giving the below request and POST to http://localhost:8080/users, a new user was created in the DB having isTeacher value 1:
{
"isTeacher" : true
}
However, when I tried to change IS_TEACHER by giving PATCH (or PUT) and this request:
{
"isTeacher" : false
}
The response showed that "isTeacher" is still true and the value didn't get changed in the table either. Can someone please let me know why this is happening?
The issue is due to #Data annotation of lombok is ignoring if you have a field that start with isXx it generates getters and setters to boolean with isTeacher for getters and setTeacher for setters then you are not able to update correctly your property, if you put "teacher" when updating should work but you should solve this by overriding that setter.
#Setter(AccessLevel.NONE) private boolean isTeacher;
public void setIsTeacher(boolean isTeacher) {
this.isTeacher = isTeacher;
}

How do I get Spring's Data Rest Repository to retrieve data by its name instead of its id

I am using Spring Data's Rest Repositories from spring-boot-starter-data-rest, with Couchbase being used as the underlining DBMS.
My Pojo for the object is setup as so.
#Document
public class Item{
#Id #GeneratedValue(strategy = UNIQUE)
private String id;
#NotNull
private String name;
//other items and getters and setters here
}
And say the Item has an id of "xxx-xxx-xxx-xxx" and name of "testItem".
Problem is, that when I want to access the item, I need to be accessible by /items/testItem, but instead it is accessible by /items/xxx-xxx-xxx-xxx.
How do I get use its name instead of its generated id, to get the data.
I found out the answer to my own question.
I just need to override the config for the EntityLookup.
#Component
public class SpringDataRestCustomization extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(UserRepository.class).
withIdMapping(User::getUsername).
withLookup(UserRepository::findByUsername);
}
}
Found the info here, though the method name changed slightly.
https://github.com/spring-projects/spring-data-examples/tree/master/rest/uri-customization
If you want query the item by name and want it perform as querying by id,you should make sure the name is unique too.You cant identify a explicit object by name if all objects have a same name,right?
With jpa you could do it like:
#NotNull
#Column(name="name",nullable=false,unique=true)
private String name;

Spring Data query over two documents

I have an m:n connection in my MongoDB, MessageUserConnection is the class between User and Message.
Now I will get all MessageUserConnections where MessageUserConnection#confirmationNeeded is true, read is false and Message#receiveDate is not older than the last week.
Is there any possibility to do this with Spring Data?
Thanks a lot!
public class MessageUserConnection {
#Id
private String id;
#DBRef
private User userCreatedMessage;
#DBRef
private Message message;
private Boolean confirmationNeeded;
private Boolean read;
}
public class Message {
#Id
private String id;
private String title;
private String message;
private DateTime receiveDate;
}
[EDIT]
I have tried it by my own:
#Query("FROM MessageUserConnection AS muc WHERE muc.confirmationNeeded = ?0 AND muc.message.receiveDate = ?1")
List<MessageUserConnection> findMessageUserConnectionByConfirmationNeededAndReceiveDate(final Boolean confirmationNeeded, final DateTime receiveDate);
and I get the following exception:
Caused by: com.mongodb.util.JSONParseException:
FROM MessageUserConnection AS muc WHERE muc.confirmationNeeded = "_param_0" AND muc.message.receiveDate = "_param_1"
Does anyone know what I am doing wrong here?
Thanks a lot!
[EDIT]
I run into another problem. My query currently looks like this.
#Query("{$and : [{'confirmationNeeded' : ?0}, {'message.receiveDate' : ?1}]}")
where confirmationNeeded is a boolean and message.receiveDate is Joda#DateTime. With this query I get the following exception:
org.springframework.data.mapping.model.MappingException: Invalid path reference message.receiveDate! Associations can only be pointed to directly or via their id property!
Does that mean that I only can join to message.id?
Thanks a lot!

Variable 'this.userInfo' is unbound and cannot be determined

I am developing a maven JDO project, but I am getting this error when I am trying to make relation between two tables (user_login, user_role)
User_Login: user_id(primary key), user_name, user_password,user_role_id
User_Role: id(primary key), role
user_role_id is same as id of user_role table
User.java:
#PersistenceCapable(table = "user_login")
public class User {
#PrimaryKey
#Column(name="user_id")
private Integer userId=0;
#Column(name="user_profile_name")
private String userProfileName=null;
#Column(name="user_email")
private String userEmail=null;
#Column(name="user_contact")
private String userContact=null;
#Column(name="user_name")
private String userName=null;
#Column(name="user_password")
private String userPassword=null;
#ManyToOne
#Column(name="user_role_id")
private Integer userRoleId=0;
Role.java:
#PersistenceCapable(table = "user_role")
public class Role {
#PrimaryKey
#Column(name="id")
private Integer id=0;
#Column(name="role")
private String role=null;
#OneToMany
private User userInfo=null;
DAOImpol:
public List<Role> getUser(String username, String userpassword) {
PersistenceManager pm = this.pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
JDOPersistenceManager jdopm = (JDOPersistenceManager)pm;
try {
// Start the transaction
tx.begin();
TypesafeQuery<User> tq = jdopm.newTypesafeQuery(User.class);
//QUser user = QUser.candidate();
QRole role = QRole.candidate();
QUser userInfo=role.userInfo;
List<Role> result = tq.filter(userInfo.userName.eq(username).and(userInfo.userPassword.eq(userpassword))).executeList();
//result = tq.executeResultList(true, user.userId);
if(result.size()>0){
log.info(">>>>>00000000"+" "+result.get(0).getUser().getUserEmail());
log.info(">>>>>11111111"+" "+result.get(0).getRoleId()+" "+result.get(0).getRole());
}else{
log.info("<<<<<<<=====000000");
}
// Commit the transaction, flushing the object to the datastore
tx.commit();
return result;
}
finally {
if (tx.isActive())
{
// Error occurred so rollback the transaction
tx.rollback();
}
pm.close();
}
I am getting this error:
javax.jdo.JDOUserException: Variable 'this.userInfo' is unbound and
cannot be determined (is it a misspelled field name? or is not intended
to be a variable?)
NestedThrowables:
org.datanucleus.exceptions.NucleusUserException: Variable
'this.userInfo' is unbound and cannot be determined (is it a
misspelled
field name? or is not intended to be a variable?)
I found that you'll get this error from JDO if you're using progaurd and progaurd renames your private fields. Adding a -keep to the progaurd config to keep the package with your Persistence Capable classes will fix it.
For example, if you keep all of your Persistence Capable classes in com.example.server.orm package you'd add this to progaurd.conf
-keep class com.example.server.orm.** {*;}

Resources