Issue with integration test for spring boot with couchdb - spring-boot

I'm pretty new to couchbase but fair experienced with spring boot. I have the following:
Repository:
import entity.Building;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
#Collection("buildings")
public interface BuildingRepository extends CouchbaseRepository<Building, String> {
}
Building:
#Document
public class Building {
#Id
#GeneratedValue(strategy = GenerationStrategy.UNIQUE)
private String id;
#Field
#NotNull
private String erf;
#Field
#NotNull
private String name;
#Field
#NotNull
private Address address;
#CreatedBy
private String creator;
#LastModifiedBy
private String lastModifiedBy;
#LastModifiedDate
private LocalDateTime lastModification;
#CreatedDate
private LocalDateTime creationDate;
#Version
private long version;
}
Test case:
#SpringBootTest
#Testcontainers
#ActiveProfiles({"test"})
#AutoConfigureMockMvc
public class RetrieveBuildingTest {
#Container
private static final CouchbaseContainer COUCHBASE = new CouchbaseContainer(
DockerImageName.parse("couchbase/server:7.1.2")).withBucket(new BucketDefinition("buildings").withPrimaryIndex(true));
#Autowired
private MockMvc mvc;
#Test
public void testLifecycleForBuildings() throws Exception {
final ClassPathResource resource = new ClassPathResource("payloads/successful-create-building.json");
final String payload = Files.readString(Path.of(resource.getURI()));
// save
mvc.perform(post("/building")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(payload))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is("Knightsbridge Mansion")));
// retrieval all
mvc.perform(get("/building"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.totalElements", is(1)));
// retrieve specific building by id
}
#DynamicPropertySource
public static void register(final DynamicPropertyRegistry register) {
register.add("spring.couchbase.connection-string", COUCHBASE::getConnectionString);
register.add("spring.couchbase.username", COUCHBASE::getUsername);
register.add("spring.couchbase.password", COUCHBASE::getPassword);
}
I see in some examples there is AbstractCouchbaseConfiguration but I'd think that the properties would have set this up. I've also set the following properties in application.properties:
spring.data.couchbase.bucket-name=buildings
spring.couchbase.env.timeouts.connect=30
spring.couchbase.env.timeouts.key-value-durable=30
Errors from the log:
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:581)
Caused by: com.couchbase.client.core.error.AmbiguousTimeoutException: InsertRequest, Reason: TIMEOUT {"cancelled":true,"completed":true,"coreId":"0xf0d3d25700000001","idempotent":false,"lastChannelId":"F0D3D25700000001/0000000058CD068A","lastDispatchedFrom":"127.0.0.1:38858","lastDispatchedTo":"localhost:49158","reason":"TIMEOUT","requestId":5,"requestType":"InsertRequest","retried":14,"retryReasons":["COLLECTION_MAP_REFRESH_IN_PROGRESS"],"service":{"bucket":"buildings","collection":"buildings","documentId":"54b991d2-fa8b-41eb-9b1e-d5519914e964","opaque":"0x15","scope":"_default","type":"kv","vbucket":233},"timeoutMs":2500,"timings":{"encodingMicros":5785,"totalMicros":2512314}}
at com.couchbase.client.core.msg.BaseRequest.cancel(BaseRequest.java:184)
at com.couchbase.client.core.msg.Request.cancel(Request.java:70)
at com.couchbase.client.core.Timer.lambda$register$2(Timer.java:157)

spring.couchbase.env.timeouts.connect and spring.couchbase.env.timeouts.key-value-durable are missing the unit. Default values are 10 seconds. If not specified then it will convert to milis and that's how the timeout is produced. In order to fix it set 30s.
See more here

Related

Consider defining a bean of type 'int' in your configuration[SpringBoot]

its my first time crating api in spring boot, i'm trying to create transaction api. when i'm running the application i'm getting this error
Description:
Parameter 0 of constructor in TransactionService.transactionService.modal.TransactionRequest required a bean of type 'int' that could not be found.
Action:
Consider defining a bean of type 'int' in your configuration.
Modal package:
TransactionEntity
#Getter
#Setter
#Builder
#Entity
public class TransactionEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int transactionId;
#NotNull
#Column(unique = true)
private UUID externalId;
#NotNull
private int userId;
#NotNull
private int merchantId;
#NotNull
private int clientReferenceId;
#NotNull
private double amount;
#Enumerated(EnumType.STRING)
#NotNull
private TransactionStatus status;
#NotNull
private String createdBy;
private String updatedBy;
#NotNull
private LocalDateTime createdAt;
#NotNull
private LocalDateTime updatedAt;
}
TransactionRequest
#Component
#Data
#Builder
public class TransactionRequest {
private int userId;
private int merchantId;
private int clientReferenceId;
private double amount;
private String createdBy;
}
TransactionResponse
#Component
#Data
#Builder
public class TransactionResponse {
private int userId;
private int merchantId;
private int clientReferenceId;
private double amount;
private LocalDateTime createdAt;
private TransactionStatus status;
}
TransactionDao
#Component
// Dao class
public class TransactionDao {
#Autowired
TransactionRepository transactionRepository;
TransactionEntity transactionEntity;
public TransactionResponse createTransaction(TransactionRequest transactionRequest){
LocalDateTime cuurentTime = LocalDateTime.now();
transactionEntity.builder().userId(transactionRequest.getUserId())
.merchantId(transactionRequest.getMerchantId())
.clientReferenceId(transactionRequest.getClientReferenceId())
.amount(transactionRequest.getAmount())
.createdBy(transactionRequest.getCreatedBy())
.createdAt(cuurentTime)
.updatedAt(cuurentTime)
.externalId(UUID.randomUUID())
.status(TransactionStatus.CREATED);
transactionRepository.save(transactionEntity);
return TransactionResponse.builder().status(transactionEntity.getStatus())
.createdAt(transactionEntity.getCreatedAt()).build();
}
}
TransactionService
#Service
public class TransactoinService {
#Autowired
public TransactionDao transactionDao;
public TransactionResponse createTransaction(TransactionRequest transactionRequest){
return transactionDao.createTransaction(transactionRequest);
}
}
TransactionController
#RestController
public class TransactionController {
#Autowired
TransactoinService transactoinService;
#PostMapping
TransactionResponse createTransaction(#RequestBody TransactionRequest transactionRequest){
return transactoinService.createTransaction(transactionRequest);
}
}
The TransactionRequest is annotated as #Component so spring boot autoscan will try to create a #Bean out that class.
It is also annotated with #Data so at the time of creating the bean Spring boot is trying to inject other beans as arguments into the all args constructor, and it is not finding an "int" bean to inject into the constructor.
I am guessing that the transaction response should not be a #Component or at least not a Singleton bean.
You should not create your POJO classes as a Spring Bean. Remove #Component annotation in your TransactionRequest and TransactionResponse POJO classes.

Spring Framework Responses from POST

What is the standard object design for accepting a POST request from a client, saving the record to the database, and then returning a response back to the client? I'm working with the Spring framework.
Should I be sending back the entity and hiding properties that aren't necessary for the response?
#RestController
public class SomeController {
private final SomeService service;
#PostMapping(value = "/post/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomeEntity> post(#RequestBody final SomeEntity someEntity) {
SomeEntity savedEntity = service.save(someEntity);
return ResponseEntity.ok(savedEntity);
}
}
#Entity
#Table(name = "posts")
public class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "body")
#JsonIgnore
private String body;
#JsonIgnore
#Column(name = "deleted_ind")
private boolean deleted;
#JsonIgnore
#Column(name = "author")
private String author;
#Column(name = "created_at")
private LocalDateTime createdAt;
}
or would I accept some sort of POST request object that I convert to an entity, then re-assemble the entity into a response?
#JsonIgnoreProperties(ignoreUnknown = true)
public class SomePostRequestResource {
private String title;
private String body;
private String createdAt;
}
#RequiredArgsConstructor
#RestController
public class SomeController {
private final SomeService service;
private final SomeResourceAssembler resourceAssembler;
#PostMapping(value = "/post/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomePostRequestResource> post(
#RequestBody final SomePostRequestResource someResource
) {
SomeEntity savedEntity = service.convertToEntityAndSave(someResource);
SomePostRequestResource response = resourceAssembler.toResource(savedEntity);
return ResponseEntity.ok(response);
}
}
But then maybe I only want to send back the createdAt, would I hide the other properties in the SomePostRequestResource, or do I need another object to represent the response, which only has the property I want to send back?
I would also appreciate any book or article suggestions related to desigining objects for use with a RESTful API. I have seen articles concerning how to design and name the endpoints, but not so many concerning how to design the objects on the backend.
I would recommend you create a DTO class for the incoming/outgoing data containing the filed that are set/viewable by the client like:
public class SomeEntityIncomingDto {
private String title;
....
}
public class SomeEntityOutgoingDto {
private Long id;
private String title;
....
}
On the other hand, You won't need to map your persistence entities to DTOs and vice versa manually, you can use a library like ModelMapper or MapStruct that handles the conversion automatically.

Spring Data JPA Redis : Cannot write custom method based query

I have configured Spring Data JPA with Redis and using RedisRepositories with provides methods like find(), findAll() etc. All these methods seem to be working just fine, but I am not able to write my custom method like.
RedisEntity findByGenderAndGrade(String gender, String grade);
RedisEntity is a simple POJO Entity class. If you want any more info, please let me know in messages.
Following is my entity:
#Data
#RedisHash("test1")
public class RedisEntity implements Serializable {
#Id
#GeneratedValue
private String id;
private String name;
private String gender;
private Integer grade;
}
Repository:
#Repository
public interface TestRepository extends JpaRepository<RedisEntity, String> {
List<RedisEntity> findAllByGender(String gender);
List<RedisEntity> findAllByGrade(Integer grade);
}
Service/Controller:
#Override
public List<RedisEntity> getById(String id) {
return testRepository.findById(id); //returns data perfectly.
}
#Override
public List<RedisEntity> getAllByGender(String gender) {
return testRepository.findAllByGender(gender); //returns []
}
#Override
public void saveEntity(RedisEntity redisEntity) {
testRepository.save(redisEntity); // saves it in redis perfectly.
}
Also,
findByGender and findAllByGender both give [], although I can see data in my redis database and save it as well.
As requested by FrançoisDupire,
#Configuration
public class RedisConfig {
#Autowired
private DeploymentProperties deploymentProperties;
private static Logger logger = LoggerFactory.getLogger(RedisConfig.class);
#Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6379);
redisStandaloneConfiguration.setPassword(RedisPassword.of("root"));
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
Also, I had referred this article: Baeldung article on Spring data redis
As mentioned by #JoshJ and verified by myself and others,
The solution to the problem is:
Adding #Indexed annotation
to all those columns/fields which need to be used with all finds.
#Data
#RedisHash("EmployeeDetails")
public class RedisEntity {
#Id
private String employeeId;
private String firstName;
private String lastName;
#Indexed
private String gender;
#Indexed
private String grade;
}
We have the Spring Data Redis Library which provides the scope to write the custom method.Attaching Sample code.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
Entity Definition
#Data
#RedisHash("EmployeeDetails")
public class RedisEntity {
#Id
private String employeeId;
private String firstName;
private String lastName;
private String gender;
private String grade;
}
Repository Definition
#Repository
public interface RedisEntityRepository extends CrudRepository<RedisEntity, String>{
List<RedisEntity> findAllByGenderAndGrade(String gender, String grade);
}
Implementation
#Component
public class RedisEntityImpl implements RedisEntityService {
#Autowired
private RedisEntityRepository redisEntityRepository;
#Override
public List<RedisEntity> getAllByGenderAndGrade(String gender, String grade) {
return redisEntityRepository.findAllByGenderAndGrade(gender,grade);
}
}
Properties
spring.cache.type = redis
spring.redis.host = localhost
spring.redis.port = 6379

How to cache Spring Data JPA Projections

I am running Spring Boot 1.5.1 with Spring Data JPA repositories. I have added a method to my User repository that makes use of JPA projections(UserProfile) which works great. I now wish to cache the results of that method in my Service layer which should return a result of type Page< UserProfile > as shown
The JPA Projection.
public interface UserProfile extends Serializable {
long getId();
#Value("#{target.firstname} #{target.othernames}")
String getFullName();
String getFirstname();
String getOthernames();
String getGender();
String getEnabled();
#Value("#{T(System).currentTimeMillis()-target.birthday.getTime()}")
long getBirthday();
}
The User Entity.
#Entity
#Cacheable(true)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User implements Serializable {
private static final long serialVersionUID = 6756059251848061768L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column
private String firstname;
#Column
private String othernames;
#Column
private String gender;
#Column
private String photoname;
#Column
private Date birthday;
#Column
private String username;
#Column
private Boolean enabled;
#Column
private String password;
#ElementCollection
private Map<String,String> phonenumbers = new HashMap<String,String>(0);
#JsonBackReference
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Address> addresses = new ArrayList<Address>(0);
//Omitted Getters and Setters
#Override
public int hashCode() {...}
#Override
public boolean equals(Object obj) {...}
}
The User repository.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
public Page<UserProfile> findAllUserProfilesBy(Pageable pageable);
}
The User service implementation.
#Service
#Transactional(readOnly=true)
public class UserServiceImpl implements UserService {
#Autowired
UserRepository UserRepository;
#Override
#Cacheable("users")
public Page<UserProfile> findAllUserProfiles(Pageable pageable) {
//simulateSlowService();
return UserRepository.findAllUserProfilesBy(pageable);
}
}
However I get the following exception when the service method gets called.
java.lang.RuntimeException: Class org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor does not implement Serializable or externalizable
How should I go about caching the result of the service method?
Any help is greatly appreciated.

want to autowire DAO class in my entity class

i have a method which i need to call in my entity class company.java.
but when i run my application it throws null pointer exception didn't fount that DAO object in entity class..
How can i get that object in entity class please help
This is my entity class..
package com.salebuild.model;
/**
* Define a company.
*
* #author mseritan
*/
#Entity
#Table(name = "company", uniqueConstraints = {#UniqueConstraint(columnNames = "name")})
#XmlRootElement
#XmlSeeAlso(value = ArrayList.class)
public class Company implements PublishableIF, Serializable, PersistableIF, HistoryIF, AddressableIF {
private static final Logger log = LoggerFactory.getLogger( Company.class );
#Autowired
private CompanyDAO companyDAO;
// CONSTANTS -----------------------------------------------------------------------------------
private static final long serialVersionUID = 1L;
// ATTRIBUTES ----------------------------------------------------------------------------------
private Long id;
#NotBlank
private String name;
private String formerName;
private CorporateTitle topLevelExec;
private List<CompanySite> sites;
private List<CompanyAlias> aliases;
#NotNull
private Industry industry;
private Company parentCompany;
private String emailTopology;
#NotNull
private Double revenue;
#NotNull
private Long numberEmployees;
private CustomerType.Type customerType;
private Boolean recruiter = false;
private int publishState;
private CompanyStatus status;
private Boolean excludeCompany = false;
private CompanyType companyType;
private String salesifyCompanyId;
private CompanySiteType companySiteType;
private String websiteUrl;
private String sourceVendor;
private String notes;
private List<CompanySpecializedRanking> specializedList = new ArrayList<CompanySpecializedRanking>();
#NotNull
private NAICSCode naicsCode;
#NotNull
private SICCode sicCode;
private Long version;
private List<Technology> technologies = new ArrayList<Technology>();
private List<CompanyContact> contacts;
private String phoneNumber;
private String faxNumber;
private String email;
private User userCreated;
private Date dateCreated;
private User userLastModified;
private Date dateLastModified;
private User userLastResearcher;
private Date dateLastResearcher;
#NotBlank
private String street1;
private String street2;
private String street3;
private String city;
private String zipCode;
private State state;
private Country country;
private String specRankingListName;
private Integer specRankingRank;
private Integer specRankingYear;
private String modifiedCompanyName;
private String formattedRevenue;
private String formattedEmployeeSize;
private List<JobPostingRaw> unconfirmedTechnologies;
// ACESSORS ------------------------------------------------------------------------------------
//getter setter for other fields //
this.specRankingYear = specRankingYear;
}
/**
* #param modifiedCompanyName
*
*/
#Column(name="modifiedCompanyName")
public String getModifiedCompanyName() {
return modifiedCompanyName;
}
public void setModifiedCompanyName(String modifiedCompanyName) {
if(modifiedCompanyName==null)
this.modifiedCompanyName=modifiedCompanyName;
else{
this.modifiedCompanyName =companyDAO.updateCompanyName(modifiedCompanyName);
}
}
#Transient
public List<JobPostingRaw> getUnconfirmedTechnologies() {
return unconfirmedTechnologies;
}
public void setUnconfirmedTechnologies(
List<JobPostingRaw> unconfirmedTechnologies) {
this.unconfirmedTechnologies = unconfirmedTechnologies;
}
}
my DAO class is like that --
package com.salebuild.dao;
import com.salebuild.model.Company;
import com.salebuild.model.search.EntitySearchCriteria;
import com.salebuild.model.search.SortedResultsPage;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public interface CompanyDAO extends CrudDAO<Company> {
Company findByNameOrAlias(String name);
List<Company> findBySearchTerm(String searchTerm, Integer start, Integer count);
// SortedResultsPage<Company> findPaged(EntitySearchCriteria criteria);
List<Long> findIds(EntitySearchCriteria criteria);
List<Company> find(Collection<Long> ids);
/**
* For just finding the company name and not looking for alias names.
*
* #param name
* #return
*/
public Company findByName( String name );
public Company findByModifiedName(String name,Company... c);
public int companyCountSinceLastLogin(Long id);
Set<Long> findDistinctIds(EntitySearchCriteria criteria);
public Integer getCompanyCountByRegion(Long regionId,List techCatIds);
List<Company> findAllCompanies(Company instance);
public List<Company> findAllModifiedCompanies(Company instance);
public String updateCompanyName(String name);
}
The easiest option is to implement factory for building entities. Then you can use AutowireCapableBeanFactory to autowire dependencies:
public abstract class GenericFactory<T> {
#Autowired
private AutowireCapableBeanFactory autowireBeanFactory;
public T createBean() {
// creation logic
autowireBeanFactory.autowireBean(createdBean);
return createdBean;
}
}
Of course you can pass object (created or retrieved) and just autowire it.
Another option is to use #Configurable - it automatically injects dependencies. http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable
You can find more details in my post: http://www.kubrynski.com/2013/09/injecting-spring-dependencies-into-non.html
JPA entities are meant to be POJOs i.e. simple Java beans which do not have dependencies and have getters and setters which contain no complex logic.
It would be better to create a service which is responsible for saving and updating your entity which is used throughout your code. This service can then be responsible for executing the logic you wish to put in your setter using dependencies which can be autowired.
The issue you have is that you Spring is not responsible for the creation of your entity. You either instantiate it using new or you obtain it from your JPA implementation. Either way there is no opportunity for Spring to autowire declared dependencies.
As an aside it's not good practice to autowire private variables. See this blog post for a fuller discussion.

Resources