Unable to query ApplicationDbContext, method takes 1 argument - mysql

I am trying to get a list of models but I can't seem to Query the ApplicationDbContext.I am getting a **No overload for method 'Query' takes 1 arguments.
Here my ApplicationDbContext
private readonly ApplicationDbContext _context;
public DapperController(ApplicationDbContext context)
{
_context = context;
}
and the GetAll method
public List<PoleModel> GetAll()
{
var obj = _context.Query<PoleModel>("Select * from dbo.GRAD").ToList();
return obj;
}
Should I use something other then List? Like IEnumerable or something? I am a bit lost as I've not worked in .NET Core before...

You appear to be trying to use a mix up of Entity Framework and Dapper. You're injecting an ApplicationDbContext, but then trying to query it with Dapper as you would a SqlConnection object instead.
To query the context with EF, you'd do something like:
public List<PoleModel> GetAll()
{
var obj = _context.PoleModelDbSet.ToList();
return obj;
}
Whereas to use Dapper, you'd inject a SqlConnection, something like:
private readonly SqlConnection _connection;
public DapperController(SqlConnection connection)
{
_connection = connection;
}
and query it something like:
public List<PoleModel> GetAll()
{
var obj = _connection.Query<PoleModel>("Select * from dbo.GRAD").ToList();
return obj;
}

You need to enable migrations for your code first database to initialize. Open package manager and type in:
Enable-migrations
Add migration initCreate
Update database

Related

How to use these #DataMongoTest and #SpringBootTest together in integration test

I am trying to write integration test case for one of my rest application which uses mongodb internally to persist the data
#DataMongoTest
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MainControllerTest {
#LocalServerPort
private int port = 8080;
/* some test cases*/
}
but I am getting below error
java.lang.IllegalStateException: Configuration error: found multiple declarations of #BootstrapWith for test class [com.sample.core.controller.MainControllerTest]: [#org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTestContextBootstrapper), #org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.context.SpringBootTestContextBootstrapper)]
looks like these two are mutually exclusive, so how to do the integration testing .
Use #AutoConfigureDataMongo with #SpringBootTest and this will resolve this ambiguity issue. #SpringBootTest and #DataMongoTest cannot be used together.
Answering to a very old post hoping it may help others.
#AutoConfigureDataMongo will connect to real database. In order to still use the embedded mongo, one can initiate the embedded mongoDb manually.
#SpringBootTest(classes = SubscriptionEventApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SubscriptionEventApiIntegrationTest {
#BeforeAll
static void setup() throws Exception {
startEmbeddedMongoDbManually();
}
private static void startEmbeddedMongoDbManually() throws IOException {
final String connectionString = "mongodb://%s:%d";
final String ip = "localhost";
final int port = 27017;
ImmutableMongodConfig mongodConfig = MongodConfig
.builder()
.version(Version.V3_5_5)
.net(new Net(ip, port, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
mongoTemplate = new MongoTemplate(MongoClients.create(String.format(connectionString, ip, port)), "test");
}
#AfterAll
static void clean() {
mongodExecutable.stop();
}
#Test
public void test() {
.....
}
}
Purushothaman suggested starting embedded MongoDB server manually. I am suggesting to start it automatically using #DataMongoTest, but creating WebTestClient manually instead.
Kotlin code below, translates to Java trivially:
#DataMongoTest
// #ContextConfiguration may not be needed for your case.
#ContextConfiguration(
classes = [
Application::class,
MainController::class,
// Add more needed classes for your tests here.
// ...
]
)
#TestPropertySource(properties = ["spring.mongodb.embedded.version=4.0.12"])
class MainControllerTest(
#Autowired
private val mainController: MainController,
// Add more beans needed for your tests here.
// ...
) {
// Creating a WebTestClient is easy and
// can be done in different ways.
// Here is one of the possible ways.
private val webTestClient: WebTestClient =
WebTestClient.bindToController(mainController).build()
#Test
fun someTest() {
// ...
}
}

How to connect to multiple MySQL databases as per the header in REST API request

I'm creating a multi tenant spring boot - JPA application.
In this application, I want to connect to MySQL Databases using DB name which is sent through API request as header.
I checked many multi tenant project samples online but still can't figure out a solution.
Can anyone suggest me a way to do this?
You can use AbstractRoutingDataSource to achieve this. AbstractRoutingDataSource requires information to know which actual DataSource to route to(referred to as Context), which is provided by determineCurrentLookupKey() method. Using example from here.
Define Context like:
public enum ClientDatabase {
CLIENT_A, CLIENT_B
}
Then you need to define Context Holder which will be used in determineCurrentLookupKey()
public class ClientDatabaseContextHolder {
private static ThreadLocal<ClientDatabase> CONTEXT = new ThreadLocal<>();
public static void set(ClientDatabase clientDatabase) {
Assert.notNull(clientDatabase, "clientDatabase cannot be null");
CONTEXT.set(clientDatabase);
}
public static ClientDatabase getClientDatabase() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
Then you can extend AbstractRoutingDataSource like below:
public class ClientDataSourceRouter extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return ClientDatabaseContextHolder.getClientDatabase();
}
}
Finally, DataSource bean configuration:
#Bean
public DataSource clientDatasource() {
Map<Object, Object> targetDataSources = new HashMap<>();
DataSource clientADatasource = clientADatasource();
DataSource clientBDatasource = clientBDatasource();
targetDataSources.put(ClientDatabase.CLIENT_A,
clientADatasource);
targetDataSources.put(ClientDatabase.CLIENT_B,
clientBDatasource);
ClientDataSourceRouter clientRoutingDatasource
= new ClientDataSourceRouter();
clientRoutingDatasource.setTargetDataSources(targetDataSources);
clientRoutingDatasource.setDefaultTargetDataSource(clientADatasource);
return clientRoutingDatasource;
}
https://github.com/wmeints/spring-multi-tenant-demo
Following this logic, I can solve it now. Some of the versions need to be upgraded and the codes as well.
Spring Boot version have changed.
org.springframework.boot
spring-boot-starter-parent
2.1.0.RELEASE
Mysql version has been removed.
And some small changed in MultitenantConfiguration.java
#Configuration
public class MultitenantConfiguration {
#Autowired
private DataSourceProperties properties;
/**
* Defines the data source for the application
* #return
*/
#Bean
#ConfigurationProperties(
prefix = "spring.datasource"
)
public DataSource dataSource() {
File[] files = Paths.get("tenants").toFile().listFiles();
Map<Object,Object> resolvedDataSources = new HashMap<>();
if(files != null) {
for (File propertyFile : files) {
Properties tenantProperties = new Properties();
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader());
try {
tenantProperties.load(new FileInputStream(propertyFile));
String tenantId = tenantProperties.getProperty("name");
dataSourceBuilder.driverClassName(properties.getDriverClassName())
.url(tenantProperties.getProperty("datasource.url"))
.username(tenantProperties.getProperty("datasource.username"))
.password(tenantProperties.getProperty("datasource.password"));
if (properties.getType() != null) {
dataSourceBuilder.type(properties.getType());
}
resolvedDataSources.put(tenantId, dataSourceBuilder.build());
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
// Create the final multi-tenant source.
// It needs a default database to connect to.
// Make sure that the default database is actually an empty tenant database.
// Don't use that for a regular tenant if you want things to be safe!
MultitenantDataSource dataSource = new MultitenantDataSource();
dataSource.setDefaultTargetDataSource(defaultDataSource());
dataSource.setTargetDataSources(resolvedDataSources);
// Call this to finalize the initialization of the data source.
dataSource.afterPropertiesSet();
return dataSource;
}
/**
* Creates the default data source for the application
* #return
*/
private DataSource defaultDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader())
.driverClassName(properties.getDriverClassName())
.url(properties.getUrl())
.username(properties.getUsername())
.password(properties.getPassword());
if(properties.getType() != null) {
dataSourceBuilder.type(properties.getType());
}
return dataSourceBuilder.build();
}
}
This change is here due to the DataSourceBuilder has been moved to another path and its constructor has been changed.
Also changed the MySQL driver class name in application.properties like this
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

Pass object with added properties in Junit Mockito

I'm trying to do the Mockito for a method called generateToken() by using MockitoJUnitRunner.class. The source which I have tried to do as follows.
#RunWith(MockitoJUnitRunner.class)
public class LoginServiceTest {
#Mock
private UserRepository userRepository;
#Mock
private JwtTokenGenerator jwtTokenGenerator;
#InjectMocks
private LoginServiceImpl loginServiceImpl = new LoginServiceImpl();
private JwtUserDto user;
private String jwtSecret;
private String username;
private String password;
/**
* Initialize test data before test cases execution
*/
#Before
public void init() {
user = new JwtUserDto();
user.setId(1L);
user.setUsername("kray1");
user.setRole("Admin");
}
#Test
public void testLogin() {
try {
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).thenReturn("myToken");
String actual = loginServiceImpl.login(username, password);
assertNotNull(actual);
} catch (Exception e) {
e.printStackTrace();
}
}
For that generateToken() method, I have to pass user object and a string. I'm declaring the user object in Init() method. When I try to execute this, the value return from the login method is null. But when I try to pass the user object as null then it will work as expected. So the problem should be with the user object.
Is there anything, like Mockito is blocking this kind of object with added properties or related thing? Please help to find a way to pass this user object with Mockito.
The LoginServiceImpl class as follows.
public class LoginServiceImpl implements LoginInterface {
#Autowired
private UserRepository userRepository;
#Autowired
private JwtTokenGenerator jwtTokenGenerator;
/*
* (non-Javadoc)
*/
public String login(String userName, String password) {
if (userName != null && password != null && !userName.isEmpty() && !password.isEmpty()) {
List<UserAuthenticationInfo> authInfo = userRepository.findUserRolesByUsernamePassword(userName, password);
if (authInfo != null && !authInfo.isEmpty()) {
JwtUserDto user = new JwtUserDto();
user.setId((long) authInfo.get(0).getUserId());
user.setUsername(userName);
user.setRole(authInfo.get(0).getUserRole());
return jwtTokenGenerator.generateToken(user, jwtSecret);
}
}
return null;
}
}
Do you have equals/hashcode on User class?
What is the result if you setup mock using
Mockito.when(jwtTokenGenerator.generateToken(any(User.class),any(String.class))
.thenReturn("myToken");
explanation:
When setting expectation as
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).then...
You instruct your mock to act only for given user object. equals method is used for that. So, if your User is missing equals method, then reference equality is used. Two User objects (each crated with separate new User() call will not be equal.
For non-matching parameters in Mockito.when your mock (thenReturn) is not applied. Default value (null) is returned from mock.
Therefore I prefer to setup mocks not for specific arguments and then use Mockito.verify to check if expected interactions with mock took place. That way your tests are more expressive. Actually most of my object have equals/hashode not because of business reasons (I do not put them in collections) but only for testing and comparing using assertEquals.
Side note:
do not catch (Exception e) { e.printStackTrace(); } in test. It is much easier just to declare test method to throw Exception. End result is same (stacktrace printed) but with less code.
You are probably creating a new JwtUserDto() in your production code or getting the user instance from another mock. If you haven't overwritten the equals() method in your JwtUserDto class your 'test' user won't equal the 'production' user.
Make sure that the production and test user are the same instance or that they .equals each other.

Is it possible to call a MySQL stored procedure from MyBatis without using a POJO?

I'd like to call a MySQL stored procedure from Java using MyBatis & Spring. Do I need to use a POJO to do it?
I'm using the following versions:
Java 1.6
MyBatis 3.2.2
Spring / MyBatis
1.2 Spring 3.2.3
MySQL 5.1
The following code snippets code does work.
Mapper XML:
<update id="calculateNonTaxableOrderAmount"
parameterType="CalculateNonTaxableAmountDTO"
statementType="CALLABLE" >
{ call sp_calc_non_taxable_order_amount(
#{orderNum,jdbcType=INTEGER,mode=IN},
#{nonTaxableAmount,jdbcType=DECIMAL,mode=OUT} )
}
</update>
The method on the DAO Interface:
public void calculateNonTaxableOrderAmount(CalculateNonTaxableAmountDTO dto);
CalculateNonTaxableAmountDTO:
public class CalculateNonTaxableAmountDTO {
private Long orderNum;
private BigDecimal nonTaxableAmount;
public Long getOrderNum() {
return orderNum;
}
public void setOrderNum(Long orderNum) {
this.orderNum = orderNum;
}
public BigDecimal getNonTaxableAmount() {
return nonTaxableAmount;
}
public void setNonTaxableAmount(BigDecimal nonTaxableAmount) {
this.nonTaxableAmount = nonTaxableAmount;
}
}
The above works great, but what I would like to do is something like this:
Mapper XML:
Note this missing parameterType attribute.
<update id="calculateNonTaxableOrderAmount"
statementType="CALLABLE" >
{ call sp_calc_non_taxable_order_amount(
#{orderNum,jdbcType=INTEGER,mode=IN},
#{nonTaxableAmount,jdbcType=DECIMAL,mode=OUT} )
}
</update>
The method on the DAO Interface:
public void calculateNonTaxableOrderAmount(
#Param("orderNum") Long orderNum,
#Param("nonTaxableAmount") BigDecimal nonTaxableAmount);
The DAO method is called with some code similar to:
BigDecimal nonTaxAmount = new BigDecimal(-1).setScale(2);
orderHeaderDAO.calculateNonTaxableOrderAmount(new Long(11111), nonTaxAmount);
System.out.println("nonTaxAmount = " + nonTaxAmount);
The code executes successfully, but the nonTaxAmount is never updated. The println prints out -1.
Any help, or guidance would be appreciated.
I believe it's not possible since MyBatis sets nonTaxableAmount value to new instance of BigDecimal, and you have reference to original BigDecimal instance. To handle this issue I use some sort of data holder (in a way resembling jax-ws data holders):
public class Holder<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data= data;
}
}
MyBatis config(asume that nonTaxableAmount is instance of Holder):
#{nonTaxableAmount.data,jdbcType=DECIMAL,mode=OUT}

mock a method call with dynamic parameter

I have a method as follows
private void validate(String schemaName){
....
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);**strong text**
Source schemaFile = new SteamSource(getClass().getClassLoader().getResourceAsStream(schemaName));
Schema schema = factory.newSchema(schemaFile);
....
}
This method get called from another method which I need to test(Using easymock and powermock). I'm struggling to mock following line
Source schemaFile = new SteamSource(getClass().getClassLoader().getResourceAsStream(schemaName));
Can someone give me a clue on this?
Current Status
Following is the mock statement
expectNew(StreamSource.class, anyObject(InputStream.class)).andReturn(mockedobject);
Powermock.replay(mockedobject, StreamSrouce.class);
This throws the following exception.
org.powermock.reflect.exceptions.TooManyConstructorsFoundException: Several matching constructors found, please specify the argument parameter types so that PowerMock can determine which method you're referring to.
Matching constructors in class javax.xml.transform.stream.StreamSource were:
I think you can do it using powermock in the following way (I'm just following the tutorial here):
Let's say you're class looks like this:
public class MyClass {
private void validate(String schemaName) {
....
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);**strong text**
Source schemaFile = new SteamSource(getClass().getClassLoader().getResourceAsStream(schemaName));
Schema schema = factory.newSchema(schemaFile);
....
}
}
You should create a test class like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClass.class)
public class MyClassTest {
private MyClass testedClass = new MyClass();
private ClassLoader mockedClassLoader = createMock(ClassLoader.class);
private InputStream mockedInputStream = createMock(InputStream.class);
#Before
public void setUp() {
PowerMock.createPartialMock(MyClass.class, "getClass");
expect(testedClass.getClass()).andReturn(mockedClassLoader);
expected(mockedClassLoader.getResourceAsStream(***You string***)).andReturn(mockedInputStream);
replayAll(); // Not sure if that's the name of the method - you need to call replay on all mocks
}
#Test
public void testValidate() {
// Run your test logic here
}
}
Please excuse me if some of the easymock methods I used are named a bit differently. But this is the basic idea.
I think you need one or a combination of the following. Use Powermock constructor mocking for the new StreamSource as documented here: Powermock MockConstructor. You will probably also need to use a mock for SchemaFactory which mean you will need to mock the static factory method call via Powermock: MockStatic