How to make Set using spring-data-aerospike - spring-boot

Environment:
spring-boot v2.0.4 RELEASE
spring-data-aerospike v2.0.1.RELEASE
java - 8
Here are my application code and properties.
// application.properties
aerospike.hosts=xxx:3000
aerospike.namespace=test
// aerospike configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(AerospikeConfiguration.AerospikeConfigurationProperties.class)
#EnableAerospikeRepositories(basePackageClassses = TestAeroRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {
private final AerospikeConfigurationProperties aerospikeConfigurationProperties;
#Override
protected Collection<Host> getHosts() {
return Host.parseServiceHosts(aerospikeConfigurationProperties.getHosts());
}
#Override
protected String nameSpace() {
return aerospikeConfigurationProperties.getNamespace();
}
#Data
#Validate
#ConfigurationProperties("aerospike")
public static class AerospikeConfigurationProperties {
#NotEmpty
String hsots;
#NotEmpty
String namespace;
}
}
# Entity class
#Value
#Document
#Builder(toBuilder = true)
#AllArgsConstructor
public class testEntity() {
#Id
int id;
#Field
String name;
#Field
String timestamp;
}
#Repository
public interface TestAeroRepository extends AerospikeRepository<TestEntity, Integer> {
}
public interface TestAeroService {
void save();
}
#Service
#RequiredArgsConstructor
public class TestAeroServiceImpl implements TestAeroService {
private final TestAeroRepository testAeroRepository;
#Override
public void save(TestEntity entity) {
testAeroRepository.save(entity);
}
}
I checked Aerospike client connection has no problem.
But error is occurred when save() method is executed.
org.springframework.cglib.core.ReflectUtils.defineClass(Ljava/lang/String;[BLjava/lang/ClassLoader;Ljava/security/ProtectionDomain;Ljava/lang/Class;)Ljava/lang/Class;
Have to make sets before execute the application? I didn't make sets.
Any problem with my code?

You’re using an old version of spring-data-aerospike (2.0.1.RELEASE was released on April 2019) is there any chance you can upgrade to the latest version? 2.4.2.RELEASE
You can see how to setup a simple spring data aerospike application here: https://medium.com/aerospike-developer-blog/simple-web-application-using-java-spring-boot-aerospike-database-and-docker-ad13795e0089
Please share the entire project’s code and the entire exception.
I would look into:
The configuration class (The Aerospike Beans creation).
The content of the testEntity class - are you using #Id annotation on the primary key field?
Extending the repository class with specifying the testEntity object (… extends AerospikeRepository<testEntity, Object> {) you can see an example in the link I added.

The set is automatically created and takes the name of your object class, which is testEntity in your case. For example, based on your code, if you do not specify a collection in the #Document annotation a set named "testEntity" will automatically be created. I added the #Document(collection = "testEntitys") annotation and all I did was create two set. Once you insert your first record, run the "SHOW SETS" aql command and it will be there. So that's one way to do it.

Related

Why I receive 404 error use Spring MVC framework?

When I send request http://localhost:8080/pets My server response 404!
The code on github: https://github.com/Teemitze/petstore
I build war file. Version spring 2.2.6.RELEASE
#Controller
#RequestMapping("/pets")
public class PetsController {
#Autowired
PetRepository petRepository;
#PostMapping("/addPet")
public void addPet(Pet pet) {
petRepository.save(pet);
}
#GetMapping
#ModelAttribute
public String pets(Model model) {
List<Pet> petList = new ArrayList<>();
petList.add(getPet());
petList.add(getPet());
petList.add(getPet());
model.addAttribute("pets", petList);
return "allPets";
}
public Pet getPet() {
Pet pet = new Pet();
pet.setId(1L);
pet.setName("Мурзик");
pet.setPrice(100);
pet.setBirthday(Date.valueOf("2019-12-12"));
pet.setSex("М");
return pet;
}
}
I checked out your code and found a few issues.
1) Package structure
Move controller, dto, repo packages to the main package (com.petstore)
Since the main application is inside the (com.petstore) package and the controller is outside the package, so it fails to scan the class.
2) Use annotation #Entity for the Pet entity class with #Id for the id property
3) Remove #ModelAttribute from pets() method since you are not binding any method parameter.
After this, I see the /pets
SpringBoot project requires define some configuration conventions that need to be follow in order to start a minimum application.
Some points you have to consider when you want to start a spring boot application.
For example:
Your SpringBootApplication(PetstoreApplication) class should be in the directory level above your other packages so that it can scan all classes.
If you want to use SpringData JPA you have to manage your model class
#Data
#Entity
public class Pet {
#Id
private long id;
private String name;
private String sex;
private Date birthday;
private byte[] photo;
private int price;
}
because it is handled by respository
public interface PetRepository extends CrudRepository<Pet, Long>
Need minimum configuration for Thymeleaf https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
You are making a GET request for a resource "/pets" so no need #ModelAttribute in get mapping method
#GetMapping()
public String allPets(Model model) {
Make sure your html files is under resources/templates directory.
Check out the reference docs
spring mvc
spring data jpa

Spring Data MongoDB BeforeSaveCallback not working

I want to have similar functionality as I get with the JPA #PrePersist but in a mongodb database. Reading the spring data mongodb documentation I found the entity callbacks: https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#entity-callbacks. They seem to work for what I need so I'm trying to implement some callbacks. I know there are some alternatives for what I'm doing (auditing annotations) but I want to keep with this for the moment.
This is how I register the callback, my entity definition and the repository:
#Configuration
public class BeforeSaveCallbackConfiguration {
#Bean
BeforeSaveCallback<Measurement> beforeSaveMeasurement() {
return (entity, document, collection) -> {
entity.setTimestamp(System.currentTimeMillis());
System.out.println("Before save, timestamp: " + entity.getTimestamp());
return entity;
};
}
}
public interface MeasurementRepository extends MongoRepository<Measurement, String> {
}
#Document
public class Measurement {
private String id;
private long timestamp;
private Float value1;
private Float value2;
// constructor, getters, setters ...
}
I save the entity using measurementRepository.save method of the repository. I actually see the printed line from the callback with the timestamp. However the data saved in the mongodb collection always have timestamp set to 0. Does anyone have any hint?
You implement BeforeConvertCallback interface can work for you:
#Component
public class TestCallBackImpl implements BeforeConvertCallback<Measurement> {
#Override
public Measurement onBeforeConvert(Measurement entity, String collection) {
entity.setTimestamp(System.currentTimeMillis());
return entity;
}
}

Spring Data Jpa custom repository no property found

I'm trying to create a repository that has a method which doesn't fit the usual JpaRepository with #Query annotations.
I've created a custom repository interface:
public interface CustomVoteRepository {
List<VoteCountResult> countVotesForSession();
}
And the implementation:
#Repository
public class CustomVoteRepositoryImp implements CustomVoteRepository {
private JdbcTemplate jdbcTemplate;
public CustomVoteRepositoryImp(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
public List<VoteCountResult> countVotesForSession() {
return jdbcTemplate.query("SELECT video_id, COUNT(votes.id) FROM votes WHERE session_id=2 GROUP BY video_id",
new CustomRowMapper());
}
}
However, this gives me this error:
No property countVotesForSession found for type Vote!
I don't understand why it's trying to map a property on the Vote class. I understand it does this for the "auto-generated" method names, but this is supposed to be a custom one.
I've come across this article: https://www.mkyong.com/spring-data/spring-data-add-custom-method-to-repository/ which explains what I'm doing, and yet it's trying to map a property of the model for a custom repository.
I'm sure I missed something stupid.
Thanks!
Edit:
Here's the VoteCountResult dto:
#Data
#AllArgsConstructor
public class VoteCountResult {
private String count;
private String title;
private String url;
}
What if you change your custom method name to votesForSessionCount ? I think this way you won't face with method name conflict.

Spring Data JPA and Generics

I have an entity that looks like this
#Entity(name = "encounter_pdf_export")
public class EncounterPDFExport<T extends Encounter> implements Serializable {
public static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long pdfExportId;
#Any(metaColumn = #Column(name = "encounter_type"))
#Cascade(CascadeType.ALL)
#AnyMetaDef(
idType = "long",
metaType = "string",
metaValues = {
#MetaValue(value = "FooEncounter", targetEntity = FooEncounter.class)
})
#JoinColumn(name = "encounter_id")
private T encounter;
The abstract type that I'm extending is:
public abstract class Encounter {
public abstract Long getEncounterId();
}
Here is my Spring Data Repository
#Repository
public interface EncounterPDFExportRepository extends PagingAndSortingRepository<EncounterPDFExport, Long> {
EncounterPDFExport findOneByEncounter_encounterId(#Param("encounterId") Long encounterId);
}
I am getting a stack trace when starting up the application related to to the findOneByEncounter_encounterId method:
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [encounter] on this ManagedType [com.iimassociates.distiller.domain.EncounterPDFExport]
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.checkNotNull(AbstractManagedType.java:144)
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.getAttribute(AbstractManagedType.java:130)
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:468)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:300)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:243)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:148)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:88)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:46)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:116)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:237)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:65)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:100)
I am assuming that either Spring Data JPA doesn't support abstracted/generic fields? If that's the case, would creating a #Query be a sufficient workaround?
Not sure if this will be helpful to anyone, but I did get this working.
Removed the abstract class and made it an interface with a single public getEncounterId() method
Modified FooEncounter to implement the above interface
Removed generics from the EncounterPDFExport class
Modified the encounter field to utilize the above interface rather than a generic
Apparently, I'm hitting some Hibernate bug/limitation when accessing fields within FooEncounter. Accessing Encounter within EncounterPDFExport works OK, though. I modified my Spring Data JPA Repository to look like the following (note the modification from finding by encounter.encounterId vs. just encounter):
#Repository
public interface EncounterPDFExportRepository extends PagingAndSortingRepository<EncounterPDFExport, Long> {
EncounterPDFExport findOneByEncounter(#Param("encounter") Encounter encounter);
}
The Hibernate bug in question seems to be related to https://jira.spring.io/browse/DATAJPA-836.

Lazy exception : size vs eager?

I was faced to a :
failed to lazily initialize a collection of role: ,no session or session was closed
When trying to access (from the controller, or a junit) the collection of "DataDictionaryEntry" that are in a "DataDictionary".
DataDictionary
#Entity
#Table( name = "IDS_RAVE_DATA_DICTIONARY",
uniqueConstraints={#UniqueConstraint(columnNames={"name"})
})
public class DataDictionary extends UnversionedObject {
#Column
private String name;
#OneToMany(mappedBy="dataDictionary",fetch=FetchType.EAGER)
private Collection<DataDictionaryEntry> dataDictionaryNames;
/* constructor */
public DataDictionary() {
super();
}
/* getters & setters */
}
DataDictionaryEntry
#Entity
#Table( name = "IDS_RAVE_DATA_DICTIONARY_ENTRY",
uniqueConstraints={#UniqueConstraint(columnNames={"dataDictionary","codedData"})
})
public class DataDictionaryEntry extends UnversionedObject {
#ManyToOne
#JoinColumn(name="dataDictionary")
private DataDictionary dataDictionary;
#Column
private String codedData;
#Column
private Integer ordinal;
#Column
private String userDataString;
#Column
private Boolean specify;
/* constructor */
public DataDictionaryEntry() {
super();
}
/* getters & setters */
}
I do have an abstract service object and another service extending it :
Generic service
#Transactional
public abstract class RaveGeneralServiceImpl<T> implements RaveGeneralService<T> {
private JpaRepository<T, Long> repo;
/**
* Init the general rave services with your specific repo
* #param repo
*/
protected void init(JpaRepository<T, Long> repo){
this.repo = repo;
}
#Override
public List<T> findAll(){
return repo.findAll();
}
#Override
public T save(T obj){
return repo.save(obj);
}
#Override
public void flush(){
repo.flush();
}
}
DataDictionaryServiceImpl
#Service
public class DataDictionaryServiceImpl extends RaveGeneralServiceImpl<DataDictionary> implements DataDictionaryService {
#Resource
private DataDictionaryRepository dataDictionaryRepository;
#PostConstruct
public void init() {
super.init(dataDictionaryRepository);
}
}
I could find replies on how to solve it. The first solution often seen is to change to LAZY to a EAGER. When I printed the generated query when accessing a FINDALL() method it shows the following :
Hibernate:
/* select
generatedAlias0
from
DataDictionary as generatedAlias0 */ select
datadictio0_.ID as ID81_,
datadictio0_.createdByUser as createdB2_81_,
datadictio0_.createdTime as createdT3_81_,
datadictio0_.lastUpdateTime as lastUpda4_81_,
datadictio0_.lastUpdateUser as lastUpda5_81_,
datadictio0_.VERSION as VERSION81_,
datadictio0_.name as name81_
from
IDS_RAVE_DATA_DICTIONARY datadictio0_
Hibernate:
/* load one-to-many com.bdls.ids.model.rave.DataDictionary.dataDictionaryNames */ select
datadictio0_.dataDictionary as dataDic11_81_1_,
datadictio0_.ID as ID1_,
datadictio0_.ID as ID82_0_,
datadictio0_.createdByUser as createdB2_82_0_,
datadictio0_.createdTime as createdT3_82_0_,
datadictio0_.lastUpdateTime as lastUpda4_82_0_,
datadictio0_.lastUpdateUser as lastUpda5_82_0_,
datadictio0_.VERSION as VERSION82_0_,
datadictio0_.codedData as codedData82_0_,
datadictio0_.dataDictionary as dataDic11_82_0_,
datadictio0_.ordinal as ordinal82_0_,
datadictio0_.specify as specify82_0_,
datadictio0_.userDataString as userDat10_82_0_
from
IDS_RAVE_DATA_DICTIONARY_ENTRY datadictio0_
where
datadictio0_.dataDictionary=?
The 2nd solution we often see is to make a call to the .size() of the component that is being lazily initialized. So indeed by changing my service to this :
#Service
public class DataDictionaryServiceImpl extends RaveGeneralServiceImpl<DataDictionary> implements DataDictionaryService {
#Resource
private DataDictionaryRepository dataDictionaryRepository;
#PostConstruct
public void init() {
super.init(dataDictionaryRepository);
}
#Override
public List<DataDictionary> findAll() {
List<DataDictionary> results = super.findAll();
for (DataDictionary dd : results) {
dd.getDataDictionaryNames().size();// init lazy
}
return results;
}
}
The lazy exception is also gone ! But the end result is the same query... So the what is the added value of keeping it LAZY if the end-query is the same ? Or did I do it wrong ?
Suppose that for the front-end you would have a data table that displays only basic information (the name for example), it would call the findAll() but still query the complete dependencies of that object ?
While the results with this method are pretty much exactly the same, the value of keeping it lazy is that if you don't need it fetched in other queries, you don't automatically have it eagerly fetched. Making the relationship eager applies to every method of accessing that entity, while calling size on a collection forces it to be fetched for that one occurrence.
There are other ways that might be more efficient, such as using a join fetch qualifier in the JPA query itself, allowing the provider to fetch the relationship using a single select.
You can either use: Hibernate.initialize() to initialize Lazy collections.
Or using spring to avoid LazyException use filer in your web.xml:
<filter>
<filter-name>hibernateFilterChain</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
But remember, using lazy fetching is bad idea if you are thinking about good application design and performance.

Resources