My account.java model is just this
package com.example.demo.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Getter
#Setter
#ToString
#Document(collection="Account")
public class Account {
#Id
private String id;
private String username;
private String password;
private String role;
}
here is my repo
package com.example.demo.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import com.example.demo.model.Account;
public interface AccountRepository extends MongoRepository<Account, Integer> {
#Query("update Account u set u.username = ?1 where u.username = ?2")
void changeUsername(String firstname, String currentName);
}
This causes the error
Caused by: com.mongodb.util.JSONParseException:
update User u set u.username = "_param_1" where u.username = "_param_2"
I'm trying to this strictly using query but how? Is there a way to use mysql query style with mongodb because thats all i'm familiar with currently. If not. How would I do this with a mongodb query?
Related
I have a list of persons in DB everyone having a CV field which is a MultiPart File in Spring. I'm trying to get all persons from db, but to ignore the CV field because I don't want to be downloaded when I use getAllPersons function. I saw a possibility with JsonIgnore but I want to ignore that field just in getAllPersons, not in other functions like getPersonById etc. How can I do it?
For that purpose you can use HQL.
i.e
interface Repo extends C... {
#Query(select h.name, h.phone, ... from Person h)
List<Person> getAllPerson();
}
Suppose that you use Sping and Spring-data you can use a projection in order to avoid maintaining custom queries. Consider the following example:
Entity class Book
#Data
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue(generator = "book_sequence", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "book_sequence", sequenceName = "book_sequence", allocationSize = 1)
private Long id;
#NaturalId
private String name;
private String author;
private String publisher;
private String plot;
#ManyToMany(mappedBy = "books")
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<BookFilter> filters = new HashSet<>();
}
Projection interface:
public interface BookNameAuthorOnly {
String getName();
String getAuthor();
}
Repository method:
#Repository
public interface BookRepository extends JpaRepository<Book, Long> {
List<BookNameAuthorOnly> findBy();
}
When the latter is invoked, the dynamic query generated by Spring, will select only the fields that you have specified in the related interface object. For more on this you can check the following documentation:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
Don't know if this fits your use case scenario but this is also another way of achieving what you need to achieve.
You can also use DTO as shown in below example:
Person entity:
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#NoArgsConstructor
#Entity
public class Person {
#Id
private long id;
private String name;
private String address;
#Lob
private Object cvFields;
}
PersonDTO:
package com.example.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#NoArgsConstructor
public class PersonDTO {
private long id;
private String name;
private String address;
public PersonDTO(Person person) {
this.id = person.getId();
this.name = person.getName();
this.address = person.getAddress();
}
}
PersonRepository:
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface PersonRepository extends JpaRepository<Person, Long> {
#Query("SELECT new com.example.dto.PersonDTO(p) FROM Person p")
List<PersonDTO> getAll();
}
I am trying to fetch all the records using JPA findAll. If I run the same query in the terminal, I get some rows as a result, but not through JPA. I tried other answers on stackoverflow, but nothing worked. I tried adding public getters and setters, although which I assume was done by the annotations.
Model class:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
#Entity
#Table(name = "tea")
public class Product {
#Id
#GeneratedValue(generator = "prod_seq", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "prod_seq", sequenceName = "seq_prod", allocationSize = 1, initialValue = 1)
#Column(name = "product_id")
private int productId;
private String name;
#Column(name = "price_per_kg")
private int pricePerKg;
private String type;
#Lob
#Column(length = 2000)
private String description;
#Column(name = "image_url")
private String imageUrl;
private String category;
}
Service class:
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.tea.exceptions.ProductNotFoundException;
import com.tea.models.Product;
import com.tea.repository.ProductRepository;
#Service
public class ProductServiceImpl implements ProductService{
#Autowired
ProductRepository productRepository;
#Override
public List<Product> getAll() throws ProductNotFoundException {
return productRepository.findAll();
}
}
Edit: Adding the repository code:
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.tea.models.Product;
public interface ProductRepository extends JpaRepository<Product,Integer >{
#Query("from Product where type like :type ")
List<Product> findByType( String type);
#Query("from Product where type =?2 and category= ?1")
List<Product> findByCategoryAndType(String category, String type);
#Query("from Product where category like :category")
List<Product> findByCategory(String category);
}
I think query should contain alias name for table like Product p and then condition like p.type.
I have the following basic objects :
/* User class */
import lombok.Data;
import lombok.AllArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id
import javax.persistence.GeneratedValue;
import javax.persistence.ManyToOne;
#Data
#Entity
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
private String surname;
#ManyToOne
private Address address;
}
/* UserComposite class, used to load partial data from a user */
import lombok.Data;
import lombok.AllArgsConstructor;
#Data
#AllArgsConstructor
public class UserComposite {
private Long userId;
private String name;
private String surname;
}
My goal here, is to update a list of User using a list of UserComposite as input. Here is what my DAO looks like :
/* UserDao class */
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
public interface UserDao extends JpaRepository<User, Long> {
#Transactional
#Modifying
#Query("update User u set u.name = uc.name , "
+ "u.surname = uc.surname "
+ "where u.id = uc.userId "
+ "and UserComposite uc in (?1)")
void updateUserFromCompositeList(List<UserComposite> userCompositeList);
}
However, this does not work. I have a hard time matching the data from the input with the data saved in my database, especially as UserComposite is not an entity.
Is there a way around this problem ?
Update multiple rows with different IDs values is not possible in one query.
Update every user by id
for (UserComposite user : List<UserComposite> userCompositeList) {
userDao.updateUser(user.getUserId(), user.getName(), user.getSurname());
}
Repository query like
#Modifying
#Query("UPDATE User u SET u.name = :name, u.surname = :surname WHERE u.id = :userId")
void updateUser(#Param("userId") int userId, #Param("name") String name, #Param("surname") String surname);
I got those two tables:
table z_users:
id
name
table z_phones:
id
iduser
phone
I got a stored procedure with this query too:
Select * from z_users INNER JOIN z_phones on z_users.id = z_phones.iduser where z_users.id = 1;
I would like to fetch this stored proc via Spring Data/Hibernate.
So i created two classes:
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Immutable;
import javax.persistence.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
#Data
#Entity
#Immutable
#NoArgsConstructor
public class ZUser {
#Id
private int id;
private String name;
public ZUser(String name, ZPhone... zPhones) {
this.name = name;
this.zPhone.addAll(Arrays.stream(zPhones).collect(Collectors.toUnmodifiableList()));
}
#OneToMany
private List<ZPhone> zPhone;
}
import lombok.Data;
import org.hibernate.annotations.Immutable;
import javax.persistence.Entity;
import javax.persistence.Id;
#Data
#Entity
#Immutable
public class ZPhone {
#Id
private int id;
private String phone;
}
I tried to call the SP with this code:
import lombok.extern.slf4j.Slf4j;
import net.resourcestorage.demojoin.database.model.ZUser;
import org.hibernate.Session;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.List;
#Slf4j
#Service
public class ZUserService {
#PersistenceContext
private EntityManager entityManager;
public List<ZUser> getUsers() {
Session session = (Session) entityManager.getDelegate();
Query query = session.createSQLQuery("CALL getUsers()").addEntity(ZUser.class);
log.info(query.getResultList().toString());
List list = query.getResultList();
return null; // don't care about this
}
}
Running this, Hibernate does this:
Hibernate: CALL getUsers()
Hibernate: select zphone0_.ZUser_id as zuser_id1_4_0_, zphone0_.zPhone_id as zphone_i2_4_0_, zphone1_.id as id1_2_1_, zphone1_.phone as phone2_2_1_ from ZUser_ZPhone zphone0_ inner join ZPhone zphone1_ on zphone0_.zPhone_id=zphone1_.id where zphone0_.ZUser_id=?
Here, hibernate should not do the select.
I start to think that I'm not following the best way, which could be the best way to achieve my goal?
Thanks!
I have a Spring project set up with JPA and Spring Data DynamoDB. It works alright. I can obtain items from the DynamoDB table by reading it by Partition Key and the Sort key (referred as DynamoDBHashKey and DynamoDBRangeKey).
My problem is that the way my repository is set up, the table is being read using query and scan operations, instead of get-item operation, which should be more efficient.
This is my entity:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#DynamoDBTable(tableName = "my-entity-table")
public class MyEntity {
#Id
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "partition_key")
private String partitionKey;
#Id
#DynamoDBRangeKey
#DynamoDBAttribute(attributeName = "sort_key")
private String sortKey;
...
}
And this is my repository:
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#EnableScan
#Repository
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
List<MyEntity> findByPartitionKeyAndSortKey(String partitionKey, String sortKey);
}
How do I configure my entity and repository to read items from the table using the get-item operation when my table has both a Partition Key and Sort Key?
Having done some research I stumbled onto this two articles:
Composite Primary Keys Kotlin Example
Spring Data JPA with a Hash & Range Key DynamoDB Table
The first one explains how to do what I want in Kotlin. Not bad, but it is not exactly what I'm looking for.
The second one hits the target perfectly, basically what it says is that I need to create a Primary Key object for my entity object, like this:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDocument;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#DynamoDBTable(tableName = "my-entity-table")
public class MyEntity {
#Id
#DynamoDBIgnore
private PrimaryKey primaryKey;
...
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "partition_key")
public String getPartitionKey() {
return primaryKey != null ? primaryKey.getPartitionKey() : null;
}
public void setPartitionKey(final String partitionKey) {
if (primaryKey == null) {
primaryKey = new PrimaryKey();
}
primaryKey.setPartitionKey(partitionKey);
}
#DynamoDBRangeKey
#DynamoDBAttribute(attributeName = "sort_key")
public String getSortKey() {
return primaryKey != null ? primaryKey.getSortKey() : null;
}
public void setSortKey(final String sortKey) {
if (primaryKey == null) {
primaryKey = new PrimaryKey();
}
primaryKey.setSortKey(sortKey);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#DynamoDBDocument
public static class PrimaryKey {
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "partition_key")
private String partitionKey;
#DynamoDBRangeKey
#DynamoDBAttribute(attributeName = "sort_key")
private String sortKey;
}
}
Then, I don't need to create any custom query methods on my repository class:
#EnableScan
#Repository
public interface MyEntityRepository extends
CrudRepository<MyEntity, MyEntity.PrimaryKey> {
}
And after that, it is just matter of using JPA's CrudRepository methods to obtain the items, like this:
final MyEntity.PrimaryKey myEntityPK
= new MyEntity.PrimaryKey("partitionKey", "sortKey");
final MyEntity myEntity = myEntityRepository.findById(myEntityPK)
.orElseThrow(() -> ... );
To verify that it actually is using the get-item operation instead of the scan and query operations, one could place a couple of breakpoints on the following classes (as of spring-data-dynamodb-5.1.0):
org.socialsignin.spring.data.dynamodb.core.DynamoDBTemplate
org.socialsignin.spring.data.dynamodb.repository.support.SimpleDynamoDBCrudRepository