I'm new on completeable-future field. Anyway I have this basic JPA application I want to use async call. But I don't want the service return into completable future.
This one works fine, but..
#Async
#Cacheable(value="distributors")
public CompletableFuture<Iterable<Distributors>> getAllDistributorsAsyncCaching() throws ExecutionException, InterruptedException {
Iterable<Distributors> result = distributorsRepository.findAll();
return CompletableFuture.completedFuture(result);
}
What I want the function to return just the iterable:
#Async
#Cacheable(value="distributors")
public Iterable<Distributors> getAllDistributorsAsyncCaching() throws ExecutionException, InterruptedException {
Iterable<Distributors> result = distributorsRepository.findAll();
return CompletableFuture.completedFuture(result);
}
I have tried with completeablefuture.get() but the problem is it becomes slow,
then I tried with completeablefuture.complete() it gives me complete(Iterable) in CompleteableFuture cannot be applied to ().
Thanks in advance!
You can put #Async on your repository method :
public interface FooRepository extends JpaRepository<Foo, Long> {
#Async
#Query("select f from Foo f")
CompletableFuture<List<Foo>> findAllFoos();
}
Then in your service you call it with :
#Service
public class FooService {
#Autowired
private FooRepository fooRepository;
public List<Foo> getFoos() {
CompletableFuture<List<Foo>> futureFoos = fooRepository.findAllFoos();
List<Foo> foos = null;
try {
foos = futureFoos.get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return foos;
}
}
Here is my test (i use h2 in-memory database) :
#RunWith(SpringRunner.class)
#SpringBootTest
public class FooTests {
#Autowired
private FooRepository fooRepository;
#Autowired
private FooService fooService;
#Before
public void setUp() {
for (int i = 0; i < 100000; i++) {
Foo foo = new Foo();
foo.setId(Long.valueOf(i));
foo.setName("foo" + i);
fooRepository.save(foo);
}
}
#Test
public void test() {
System.out.println(fooService.getFoos().size());
}
}
You can put #Cacheable on service method (here is getFoos)
Related
I am following this article to implement a database read/write separation feature by calling different methods. However, I got the error:
Missing method call for verify(mock) here: verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY);
when doing the testing.
My test case is trying to verify DatabaseEnvironment.READONLY has been set once when using TransactionReadonlyAspect AOP annotation:
// TransactionReadonlyAspectTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {LoadServiceImpl.class, TransactionReadonlyAspect.class})
public class TransactionReadonlyAspectTest {
#Autowired
private TransactionReadonlyAspect transactionReadonlyAspect;
#MockBean
private LoadServiceImpl loadService;
#Test
public void testReadOnlyTransaction() throws Throwable {
ProceedingJoinPoint mockProceedingJoinPoint = mock(ProceedingJoinPoint.class);
Transactional mockTransactional = mock(Transactional.class);
DatabaseContextHolder spyDatabaseContextHolder = mock(DatabaseContextHolder.class);
when(mockTransactional.readOnly()).thenReturn(true);
when(loadService.findById(16)).thenReturn(null);
when(mockProceedingJoinPoint.proceed()).thenAnswer(invocation -> loadService.findById(16));
transactionReadonlyAspect.proceed(mockProceedingJoinPoint, mockTransactional);
verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY); // got the error: Missing method call for verify(mock)
verify(loadService, times(1)).findById(16);
assertEquals(DatabaseContextHolder.getEnvironment(), DatabaseEnvironment.UPDATABLE);
}
}
//TransactionReadonlyAspect.java
#Aspect
#Component
#Order(0)
#Slf4j
public class TransactionReadonlyAspect {
#Around("#annotation(transactional)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint,
org.springframework.transaction.annotation.Transactional transactional) throws Throwable {
try {
if (transactional.readOnly()) {
log.info("Inside method " + proceedingJoinPoint.getSignature());
DatabaseContextHolder.set(DatabaseEnvironment.READONLY);
}
return proceedingJoinPoint.proceed();
} finally {
DatabaseContextHolder.reset();
}
}
}
// DatabaseContextHolder.java
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseEnvironment> CONTEXT = new ThreadLocal<>();
public static void set(DatabaseEnvironment databaseEnvironment) {
CONTEXT.set(databaseEnvironment);
}
public static DatabaseEnvironment getEnvironment() {
DatabaseEnvironment context = CONTEXT.get();
System.out.println("context: " + context);
return CONTEXT.get();
}
public static void reset() {
CONTEXT.set(DatabaseEnvironment.UPDATABLE);
}
}
//DatabaseEnvironment.java
public enum DatabaseEnvironment {
UPDATABLE,READONLY
}
// LoadServiceImpl.java
#Service
public class LoadServiceImpl implements LoadService {
#Override
#Transactional(readOnly = true)
public LoadEntity findById(Integer Id) {
return this.loadDAO.findById(Id);
}
...
}
I just want to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) has been used once then in the TransactionReadonlyAspect finally block it will be reset to DatabaseEnvironment.UPDATABLE which make sense.
However, how to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) gets called once? Why does this error occur? Is there a better way to test TransactionReadonlyAspect?
I am quite new in Spring and I am facing an issue right now with testing:
I have the following Service:
#Service
public class MyService {
public Integer getKey() {
List<Integer> keys = getKeys(1);
if (keys.size() == 1) {
return keys.get(0);
}
throw new IllegalArgumentException("Error!");
}
... and a getKeys() method, which provides a list based ona rest call...
}
And I use this service class in antother class:
#NoArgsConstructor
public class MyOtherClass extends MyClass {
#Autowired
private MyService myService;
....
#Override public KeyValue<Object, Object> doSomething(Object key, Object value) {
if (conditionIsTrue(key, value)) {
MyObject obj = new MyObject();
myObject.setKey(keyService.getKey()); ----- here is always null the keyService
.....
} else {
return KeyValue.pair(null, null);
}
}
And I try to write a test but the MyService is always null..
#ActiveProfiles("my-test")
#SpringBootTest(classes = Application.class)
#Testcontainers
#Slf4j
public class MyTest extends TestContext {
#BeforeEach
void init(final TestInfo testInfo) {
....
}
#AfterEach
void deinit() {
....
}
#Test
public void myTest() {
....
}
How can I inject a mock MyService into the test container?
Thank you!
I have a Spring Service like below:
#Service
public class SendWithUsService
{
private SendWithUs mailAPI;
public SendWithUsService()
{
this.mailAPI = new SendWithUs();
}
public void sendEmailEvent(Dto data)
{
try
{
SendWithUsSendRequest request = new SendWithUsSendRequest()...;
mailAPI.send(request);
}
catch (Exception e)
{
...
}
}
}
And my test code look like below:
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"javax.net.ssl.*"})
#PrepareForTest(SendWithUsService.class)
public class SendWithUsServiceTest
{
#InjectMocks
private SendWithUsService sendWithUsService;
#Mock
private SendWithUs mailAPI;
#Test
public void sendEmailEvent_successfully() throws Exception
{
whenNew(SendWithUs.class).withAnyArguments().thenReturn(mailAPI);
Dto emailData = ...;
sendWithUsService.sendEmailEvent(emailData);
...
}
}
In here, PowerMock whenNew method doesn't work. But when I call it outside of constructor like inside the sendEmailEvent method, it is triggered.
Is there a way to handle it?
Works:
public void sendEmailEvent(Dto data)
{
this.mailAPI = new SendWithUs();
...
}
Not works:
public SendWithUsService()
{
this.mailAPI = new SendWithUs();
}
I've solved it like below:
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"javax.net.ssl.*"})
#PrepareForTest(SendWithUsService.class)
public class SendWithUsServiceTest
{
#InjectMocks
private SendWithUsService sendWithUsService;
#Mock
private SendWithUs mailAPI;
#Before
public void setUp() throws Exception {
whenNew(SendWithUs.class).withAnyArguments().thenReturn(mailAPI);
MockitoAnnotations.initMocks(this);
}
#Test
public void sendEmailEvent_successfully() throws Exception
{
Dto emailData = ...;
sendWithUsService.sendEmailEvent(emailData);
...
}
}
I need help with this scenario, in theory the isolation level of Serializable should stop delete from happening, but in this scenario it still deletes the row with id 1, I have tried #EnableTransactionManagement and isolation repeatable read, it still doesn't block the delete nor cause the delete to throw exception
In summary, I need to stop any delete invocation whenever the update method is still ongoing
I am using H2 in memory database for this sample
Thanks
Entity:
public class Something {
#Id
private Integer id;
private String name;
private String desc;
}
Repo:
public interface SomeRepository extends JpaRepository<Something, Integer> {
}
Service:
#Service
public class SomeService {
#Autowired
private SomeRepository someRepository;
public void deleteSomething2(Something something) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
someRepository.delete(something);
}
#Transactional(isolation = Isolation.SERIALIZABLE)
public void updateSomething2(Something something) {
Something something1 = someRepository.findById(1).get();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
something1.setName("namanama");
someRepository.saveAndFlush(something1);
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class DemoApplicationTests {
#Autowired
private SomeService service;
#Test
public void test() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Future> futures = new ArrayList<>();
futures.add(executorService.submit(() -> service.updateSomething2(Something.builder().id(1).name("namaone").build())));
futures.add(executorService.submit(() -> service.deleteSomething2(Something.builder().id(1).build())));
while(futures.stream().filter(f -> f.isDone() == false).count() > 0) {
Thread.sleep(3000);
}
List<Something> all = service.findAll();
System.out.println(all);
}
}
I'm learning how to integrate Spring with GWT and RequestFactory by doing this following example. I got a NullPointerException and I don't know why. Can anyone help me?
Here is my code:
#Repository
public class EmployeeDAO implements IEmployeeDAO {
#PersistenceContext
private EntityManager entity;
#Override
public Employee findById(Long id) {
Query query = entity.createQuery("from Employee where id = :param");
query.setParameter("param", id);
query.setMaxResults(1);
return (Employee) query.getSingleResult();
}
#Transactional(propagation = Propagation.REQUIRED)
#Override
public void save(Employee employee) {
entity.merge(employee);
}
#Override
public void remove(Employee employee) {
entity.remove(employee);
}
#SuppressWarnings("unchecked")
#Override
public List<Employee> getAllEmployee() {
Query query = entity.createQuery("from Employee");
return query.getResultList();
}
// ...
}
and:
#Service(value = IEmployeeDAO.class, locator = DaoLocator.class)
public interface EmployeeRequestContext extends RequestContext {
Request<EmployeeProxy> findById(Long id);
Request<Void> save(EmployeeProxy employee);
Request<Void> remove(EmployeeProxy employee);
Request<List<EmployeeProxy>> getAllEmployee();
Request<EmployeeProxy> findOneByName(String name);
}
and:
#ProxyFor(Employee.class)
public interface EmployeeProxy extends EntityProxy {
Long getId();
String getName();
String getSurname();
void setId(Long id);
void setName(String name);
void setSurname(String surname);
Long getVersion();
void setVersion(Long version);
}
The NullPointerException is throw in GWT Entry Point in method:
protected void refresh() {
context = createFactory().employeeRequest();
final EmployeeProxy ep = context.create(EmployeeProxy.class);
ep.setName("Jan");
ep.setSurname("Kowalski");
ep.setVersion(new Long(0));
context.save(ep).fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
employeeList.add(ep);
}
#Override
public void onFailure(ServerFailure error) {
System.out.println("error podczas zapisu");
}
});
context = createFactory().employeeRequest();
context.getAllEmployee().fire(new Receiver<List<EmployeeProxy>>() {
#Override
public void onSuccess(List<EmployeeProxy> response) {
System.out.println(" " + response); // NULL
}
#Override
public void onFailure(ServerFailure error) {
}
});
System.out.println("Bedziemy wyswietlac dane!");
updateTable(employeeList);
}
the last one: method which create Factory:
private static EmployeeRequestFactory createFactory() {
EmployeeRequestFactory factory = GWT.create(EmployeeRequestFactory.class);
factory.initialize(new SimpleEventBus());
return factory;
}
Please help me...
Please print the stacktrace for the NullPointerException. Only then can we analyze the cause for the exception.