Spring boot circular reference with multi-tenant db setup - spring-boot

It's another one of those circular reference errors in Spring Boot. My log says:
userDetailsServiceImpl defined in file [/home/.../auth/UserDetailsServiceImpl.class]
┌─────┐
| tenantDataSource
└─────┘
Now, I've seen this happening to other people, but never with one class. Here comes the TenantDataSource:
#Component
class TenantDataSource(private val configRepository: DataSourceConfigRepository): Serializable {
private val dataSources = HashMap<String, DataSource>()
val hexKeyMaterials = HashMap<String, String>()
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
fun getDataSource(dataSourceName: String): DataSource {
logger.info(" [X] TenantDataSource - Getting DataSource: $dataSourceName")
if (!dataSources.contains(dataSourceName)) {
logger.info(" [X] TenantDataSource - DataSource $dataSourceName is not in map... Creating...")
dataSources[dataSourceName] = createDataSource(dataSourceName)
}
return dataSources[dataSourceName]!!
}
#PostConstruct
fun getAll(): Map<String, DataSource> {
logger.info(" [X] TenantDataSource - Processing all datasources after construct")
val configList: List<DataSourceConfig> = configRepository.findAll()
val result: MutableMap<String, DataSource> = HashMap()
for (config in configList) {
result[config.name] = getDataSource(config.name)
hexKeyMaterials[config.name] = config.keymaterial
}
return result
}
private fun createDataSource(dataSourceName: String): DataSource {
logger.info(" [X] TenantDataSource - Creating dataSource from config: $dataSourceName")
val config: DataSourceConfig = configRepository.findByName(dataSourceName)
return DataSourceBuilder
.create()
.username(config.username)
.password(config.password)
.url(config.url).build()
}
}
I can't imagine where I should start looking for the circular reference in this... :( I've seen some people saying "i don't even have constructor injection..." Well, I do because I don't like the autowired lateinit variables in Kotlin, that's all. So if there's a chance I'd like to keep my CIs. Help me out here because I don't get what's going on! :)
Oh, the UserDetailsServiceImpl if it helps:
#Service
class UserDetailsServiceImpl(private val userRepository: UserRepository, private val decrypter: HaliteDecrypter, private val tenantDataSource: TenantDataSource): UserDetailsService {
private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
override fun loadUserByUsername(username: String): AuthUser {
logger.info("Trying to get user for authentication $username")
val user = userRepository.getUserByUsername(username)
decrypter.hexKeyMaterial = tenantDataSource.hexKeyMaterials[TenantContext.currentTenant.get()]!!
user?.let {
logger.info("Returning UserDetails for " + user.username)
return AuthUser(
user.userId,
user.username,
decrypter.decryptPasswd(user.password).toString(Charsets.US_ASCII),
user.role.id,
user.role.name,
user.flags
)
} ?: throw UsernameNotFoundException(String.format("Username %s not found", username))
}
}

Related

How to get a data from mongoDB with the _id in Kotlin

I'm new in programming and I'm had having some problems to create an API for Kotlin getting data from a existing DB from mongoDB to my API.
I'm making Tests and I want to return a data with it Id, but I can't.
I'm using the SpringBoot, JPA, and mongoDB Data. The host is my machine, the database name is ArthurLeywin, and the collection name is Capitulos.
I'm receiving the error status=404, error=Not Found, path=/ArthueLeywin/Capitulos/6306e6417d5b2e259951f292}
The code of the Test is
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ExtendWith(SpringExtension::class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TbateMobileReederApplicationTests #Autowired constructor(
private val capituloRepository: CapituloRepository,
private val restTemplate: TestRestTemplate
) {
private val defaultCapituloId : ObjectId = ObjectId("6306e6417d5b2e259951f292")
//ObjectId.get()
#LocalServerPort
protected var port: Int = 27017
private fun getRootUrl(): String? = "http://localhost:$port/ArthueLeywin/Capitulos"
#Test
fun `should return single cap by id`() {
val response = restTemplate.getForObject(
getRootUrl() + "/$defaultCapituloId",
Object::class.java
)
val titleResponse = response.toString()
println(titleResponse)
}
}
My controller, if it's needed is
#RestController
#RequestMapping("/Capitulos")
class CapituloController (
private val capituloRepository : CapituloRepository
){
#GetMapping
fun getAllCapitulos() : ResponseEntity<List<Capitulo>>{
val capitulos = capituloRepository.findAll()
return ResponseEntity.ok(capitulos)
}
#GetMapping("/{id}")
fun getOneCapitulo(#PathVariable("id") id: String): ResponseEntity<Capitulo> {
val capitulo = capituloRepository.findOneById(ObjectId(id))
return ResponseEntity.ok(capitulo)
}
#PostMapping
fun createCapitulo (#RequestBody request: CapituloRequest) : ResponseEntity<Capitulo>{
val capitulo = capituloRepository.save(Capitulo(
nomeCapitulo = request.nomeCapitulo,
urlString = request.urlString,
novelTexto = request.novelTexto
))
return ResponseEntity(capitulo, HttpStatus.CREATED)
}
}
As I said I'm new in this, so please help me with explanations <3

Spring test #Transactional with #Nested junit classes

I am encountering a problem in spring transactions: even though I see in the output that a transaction is created and rolled back after each test method; when I check the database content, it still keeps the document that was saved during the transaction.
I ran mongodb instance in replica mode following instructions from here https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set-for-testing/.
My test class is defined as follows:
#Transactional
#AutoConfigureMockMvc
#ActiveProfiles(profiles = ["test"])
#DisplayName("Login Functionality Test")
#SpringBootTest(classes = [TestConfiguration::class])
class LoginFunctionalityTest #Autowired constructor(
private val mockMvc: MockMvc,
private val userRepository: UserRepository,
private val openUserRepository: OpenUserRepository,
private val patientRepository: PatientRepository,
private val passwordEncoder: PasswordEncoder,
private val jwtMatcher: JWTMatcher,
#Value("\${secret:sD1fRUWtBdfA8BNcbf}") private val fastLoginSecret: String
) {
private val existingUsersPassword = "SOME_PASSWORD"
private val userFastLoginTokenSecret = "ANY_SECRET"
private lateinit var existingUser: User
private lateinit var existingOpenUser: OpenUser
private lateinit var existingPatient: Patient
#BeforeEach
fun prepareContext() {
println("Init db")
existingUser = userRepository.save(User(null, now(), "test", "test", "test#test.test", passwordEncoder.encode(existingUsersPassword), UserAuthority.Patient, true, true))
existingOpenUser = openUserRepository.save(OpenUser(null, "ANY", nextExpiryDate()))
existingPatient = patientRepository.save(Patient(null, existingUser))
}
#DisplayName("Login Controller")
#Nested
inner class LoginControllerTest{
#Test
fun `should return unauthorized 401 status code when user logins with not existing email`() {
val invalidEmail = "INVALID_EMAIL"
assertThrows<EmptyResultDataAccessException> { userRepository.findByEmail(invalidEmail) }
mockMvc.post("/auth/login") {
param("username", invalidEmail)
param("password", existingUsersPassword)
}.andExpect {
jsonPath("$.status", equalToIgnoringCase(HttpStatus.UNAUTHORIZED.name))
jsonPath("$.error",StringContains(true,"Invalid login credentials"))
jsonPath("$.data", nullValue())
status { isUnauthorized() }
}
}
...
}
#ActiveProfiles(profiles = ["open-user"])
#DisplayName("Open User Login Controller")
#Nested
inner class OpenUserControllerLoginTest(){
#Test
fun `should return bad request 400 status when any of the required login parameters are missing`(){
val validFastLoginToken = String(Base64.getEncoder().encode(passwordEncoder.encode("$userFastLoginTokenSecret${existingUser.passwordHash}$fastLoginSecret").toByteArray()))
mockMvc
.post("/auth/login/fast") {
param("userId", existingUser.id!!)
param("secret", userFastLoginTokenSecret)
param("fastLoginToken", validFastLoginToken)
}.andExpect {
jsonPath("$.data.isUserActivated", equalTo(existingUser.activated))
jsonPath("$.data.accessToken",jwtMatcher.matches(existingUser))
jsonPath("$.status", equalToIgnoringCase(HttpStatus.OK.name))
jsonPath("$.error", nullValue())
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON) }
}
}
...
}
The output in console has the "Init db" printed between creation and roll back of transaction.
It works correctly only if I move #ActiveProfiles from #Nested class to the outer class. Seems like #ActiveProfiles causes context reload but I don't know why this causes problems with transactions.

AWS Spring can't set object mapper

im trying to set jackson serializer at spring MessageTemplate to send messages alredy formated using jacksonMapper and SQS/SNS, the problem is that it keeps sending messages unformated, even with notation #JsonProperty or #JsonFormat
here is my configs:
#Configuration
class AwsMessagingConfigurationLocal(
#Value("\${cloud.aws.region.static}") val region: String,
#Value("\${cloud.aws.credentials.accessKey}") val accessKey: String,
#Value("\${cloud.aws.credentials.secretKey}") val secretKey: String,
#Value("\${cloud.aws.endpoint}") val endpoint: String
) {
#Bean
fun notificationMessageTemplate(): NotificationMessagingTemplate {
return NotificationMessagingTemplate(buildAmazonSNSAsync())
}
#Bean
fun queueMessagingTemplate(): QueueMessagingTemplate {
return QueueMessagingTemplate(buildAmazonSQSAsync())
}
#Bean
fun simpleMessageListenerContainerFactory(): SimpleMessageListenerContainerFactory {
val messageListenerContainerFactory = SimpleMessageListenerContainerFactory()
messageListenerContainerFactory.setAmazonSqs(buildAmazonSQSAsync())
return messageListenerContainerFactory
}
#Bean
fun queueMessageHandlerFactory(objectMapper: ObjectMapper): QueueMessageHandlerFactory {
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper.registerModule(JavaTimeModule())
val queueMessageHandlerFactory = QueueMessageHandlerFactory()
queueMessageHandlerFactory.messageConverters = listOf(messageConverter)
queueMessageHandlerFactory.setArgumentResolvers(listOf(NotificationMessageArgumentResolver(messageConverter)))
return queueMessageHandlerFactory
}
private fun buildAmazonSNSAsync(): AmazonSNSAsync {
return AmazonSNSAsyncClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, secretKey)))
.build()
}
private fun buildAmazonSQSAsync(): AmazonSQSAsync {
return AmazonSQSAsyncClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, secretKey)))
.build()
}
}
but when i send an object with LocalDateTime, even with #JsonFormat it keeps sending wrong format:
data class Blob(
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
val timestamp: LocalDateTime
)
i made this simple dispatch function to send messages through SNS(topic) and connected a queue to receive the responses.
class Dispatcher(
private val private val notificationTemplate: NotificationMessagingTemplate
)
fun dispatch(blob: Blob) {
val header = mapOf("randon" to "message")
notificationTemplate.convertAndSend("topic", blob, header)
}
wanted result :
\"timestamp\"=\"2021-11-26T19:18:46.905505\"
current result :
"timestamp\":{\"nano\":913070000,\"year\":2021,\"monthValue\":11,\"dayOfMonth\":26,\"hour\":19,\"minute\":18,\"second\":46,\"month\":\"NOVEMBER\",\"dayOfWeek\":\"FRIDAY\",\"dayOfYear\":330,\"chronology\":{\"calendarType\":\"iso8601\",\"id\":\"ISO\"}}
i don't have any clue about what is happening.
PS. i tested SQS too, and the result is the same.
after searching inside awspring, i found that the SimpleMessageConverter is set by default, after some research, i found the right way to set the custom mapper at sendAndConvert() function. here is the configurtions:
#Bean
fun notificationMessageTemplate(objectMapper: ObjectMapper): NotificationMessagingTemplate {
// here we set the custom message converter
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper
// as we want to send it as a Json, we need to set the serialization to String
messageConverter.serializedPayloadClass = String::class.java
val notificationMessageTemplate = NotificationMessagingTemplate(buildAmazonSNSAsync())
// here is where we set the proper message converter
notificationMessageTemplate.messageConverter = messageConverter
return notificationMessageTemplate
}
#Bean
fun queueMessagingTemplate(objectMapper: ObjectMapper): QueueMessagingTemplate {
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper
messageConverter.serializedPayloadClass = String::class.java
val queueMessageConverter = QueueMessagingTemplate(buildAmazonSQSAsync())
queueMessageConverter.messageConverter = messageConverter
return queueMessageConverter
}
after setting this mappers, every notation will be working fine.
here is the result:
\"timestamp\":\"2021-11-29T17:46:52Z\"

How to inject spring resources into Apache Ignite classes?

I have a problem with injection spring beans into some ignite's classes. I'm trying to create this: Client -> Apache Ignite -> Spring-Data -> DataBase
Maybe it is wrong, i'm not sure.
So, at this moment my classes look like:
AppConfiguration
#Configuration
#ComponentScan(basePackages = arrayOf("com.ignite.cache"))
open class AppConfiguration : Serializable {
private val logger: Logger = Logger.getLogger(AppConfiguration::class.java)
#Bean
open fun igniteInstance(): Ignite {
val cfg = IgniteConfiguration()
cfg.igniteInstanceName = "springDataNode"
cfg.isPeerClassLoadingEnabled = true
var clientCache: CacheConfiguration<Long, Client> = CacheConfiguration("ClientCache")
clientCache.apply {
setIndexedTypes(Long::class.java, Client::class.java)
setCacheStoreFactory(FactoryBuilder.factoryOf(ClientStore::class.java))
isReadThrough = true
isWriteThrough = true
}
cfg.setCacheConfiguration(clientCache)
return Ignition.start(cfg)
}
}
DataSourceConfiguration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = arrayOf("com.ignite.cache.model.repositories.springdatarepository"))
#EnableIgniteRepositories(basePackages = arrayOf("com.ignite.cache.model.repositories.igniterepository"))
#ComponentScan(basePackages = arrayOf("com.ignite.cache.model"))
open class DataSourceConfiguration : Serializable {
#Bean
open fun entityManagerFactory(): LocalContainerEntityManagerFactoryBean {
var entityManagerFactory: LocalContainerEntityManagerFactoryBean = LocalContainerEntityManagerFactoryBean()
entityManagerFactory.apply {
dataSource = dataSource()
setPackagesToScan("com.ignite.cache.model")
var vendorAdapter: HibernateJpaVendorAdapter = HibernateJpaVendorAdapter()
vendorAdapter.apply {
setGenerateDdl(true)
setShowSql(true)
}
var properties: Properties = Properties()
properties.apply {
put("database.dialet", "org.hibernate.dialect.PostgreSQL95Dialect")
put("database.globally_quoted_identifiers", "false")
put("database.enable_lazy_load_no_trans", "true")
put("database.show_sql", "true")
}
jpaVendorAdapter = vendorAdapter
setJpaProperties(properties)
}
return entityManagerFactory
}
#Bean
open fun dataSource(): DataSource {
var source: ComboPooledDataSource = ComboPooledDataSource()
source.apply {
driverClass = "org.postgresql.Driver"
jdbcUrl = "jdbc:postgresql://localhost:5432/ignite"
user = "postgres"
password = "1111"
acquireIncrement = 5
idleConnectionTestPeriod = 60
maxPoolSize = 20
minPoolSize = 10
initialPoolSize = 10
}
return source
}
#Bean
open fun transactionManager() : PlatformTransactionManager {
var manager: JpaTransactionManager = JpaTransactionManager()
manager.apply {
entityManagerFactory = entityManagerFactory().nativeEntityManagerFactory
}
return manager
}
#Bean
open fun exceptionTranslator(): PersistenceExceptionTranslationPostProcessor = PersistenceExceptionTranslationPostProcessor()
}
Entity:
#Entity
#Table(name = "client")
data class Client
(
#Id
#Column(name = "id")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
var id: Long = 0,
#Column(name = "email", nullable = false, unique = true)
var email: String = "",
#Column(name = "login", nullable = false, unique = true)
var login: String = ""
) : Serializable
Repositories:
#RepositoryConfig(cacheName = "ClientCache")
interface IgniteClientRepository : IgniteRepository<Client, Long> {
}
#Repository
interface ClientRepository : CrudRepository<Client, Long>
Standart implemenation for service classes and CacheStore for ignite:
public class ClientStore implements CacheStore<Long, Client>, Serializable {
private Logger logger = Logger.getLogger(ClientStore.class);
#SpringResource
private IClientService clientRepository; // <- error is here (NPE)
#Override
public void loadCache(IgniteBiInClosure<Long, Client> igniteBiInClosure, #Nullable Object... objects) throws CacheLoaderException {
Iterable<Client> clients = clientRepository.findAll();
for(Client client : clients) {
igniteBiInClosure.apply(client.getId(), client);
}
}
...
}
Main:
public class Main {
private static Logger logger = Logger.getLogger(Main.class);
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfiguration.class, DataSourceConfiguration.class);
Ignite ignite = context.getBean(Ignite.class);
(ignite.getOrCreateCache("ClientCache")).loadCache(null);
IgniteClientRepository clientRepository = context.getBean(IgniteClientRepository.class);
Iterable<Client> clients = clientRepository.findAll();
for(Client client : clients) {
logger.info(client);
}
logger.info("Finish");
}
}
But when i try to load some data from database into cache i get error NPE:
Exception in thread "main" javax.cache.integration.CacheLoaderException: java.lang.NullPointerException
at org.apache.ignite.internal.processors.cache.store.GridCacheStoreManagerAdapter.loadCache(GridCacheStoreManagerAdapter.java:528)
at org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.localLoadCache(GridDhtCacheAdapter.java:486)
at org.apache.ignite.internal.processors.cache.GridCacheProxyImpl.localLoadCache(GridCacheProxyImpl.java:217)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$LoadCacheJob.localExecute(GridCacheAdapter.java:5439)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$LoadCacheJobV2.localExecute(GridCacheAdapter.java:5488)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$TopologyVersionAwareJob.execute(GridCacheAdapter.java:6103)
at org.apache.ignite.compute.ComputeJobAdapter.call(ComputeJobAdapter.java:132)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor$C2.execute(GridClosureProcessor.java:1842)
at org.apache.ignite.internal.processors.job.GridJobWorker$2.call(GridJobWorker.java:566)
at org.apache.ignite.internal.util.IgniteUtils.wrapThreadLoader(IgniteUtils.java:6621)
at org.apache.ignite.internal.processors.job.GridJobWorker.execute0(GridJobWorker.java:560)
at org.apache.ignite.internal.processors.job.GridJobWorker.body(GridJobWorker.java:489)
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110)
at org.apache.ignite.internal.processors.job.GridJobProcessor.processJobExecuteRequest(GridJobProcessor.java:1114)
at org.apache.ignite.internal.processors.task.GridTaskWorker.sendRequest(GridTaskWorker.java:1379)
at org.apache.ignite.internal.processors.task.GridTaskWorker.processMappedJobs(GridTaskWorker.java:640)
at org.apache.ignite.internal.processors.task.GridTaskWorker.body(GridTaskWorker.java:532)
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110)
at org.apache.ignite.internal.processors.task.GridTaskProcessor.startTask(GridTaskProcessor.java:743)
at org.apache.ignite.internal.processors.task.GridTaskProcessor.execute(GridTaskProcessor.java:443)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:447)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:418)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:402)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter.globalLoadCacheAsync(GridCacheAdapter.java:3681)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter.globalLoadCache(GridCacheAdapter.java:3657)
at org.apache.ignite.internal.processors.cache.IgniteCacheProxy.loadCache(IgniteCacheProxy.java:387)
at com.ignite.cache.Main.main(Main.java:22)
Caused by: java.lang.NullPointerException
at com.ignite.cache.model.service.ClientStore.loadCache(ClientStore.java:30)
at org.apache.ignite.internal.processors.cache.store.GridCacheStoreManagerAdapter.loadCache(GridCacheStoreManagerAdapter.java:502)
... 26 more
I still can't figure out why my client service doesn't inject into CacheStore class. Maybe i should use xml config instead of java-class config for ignite?
When you start Ignite using Ignition#start with IgniteConfiguration object, it's not aware of Spring context at all. You need to use IgniteSpring#start methods instead to provide context explicitly. Another option is to utilize IgniteSpringBean that already implements ApplicationContextAware and starts Ignite instance properly.
Also note that you will need to provide either bean name or bean class as a parameter to #SpringResource annotation.
I don't know Ignite and I don't know the programming language you are using but it looks like Ignite and not Spring is creating your ClientStore instances. Therefore you need to inject the dependencies manually.
I'm not really familiar with the FactoryBuilder used there, so there might be nice solutions if you can use a constructor with arguments or even a lambda, but if that doesn' work, you can store a reference to the repository in static field and get it from there in the constructor of the ClientStore.

Neo4J repository not wired everywhere

I've defined a Neo4j repository as follow (code is in Kotlin but it's very close to Java) :
#Repository
interface UserRepository : GraphRepository<User> {
fun findByEmail(email: String): User?
#Query("match (n:User)-[:IS_AUTH]->(:Permission {name: {0}}) where id(n) = {1} return n")
fun authorizedUser(permission: String, userId: Long): User?
}
In a controller, I've written:
#Controller
#RequestMapping("/company")
open class CreateCompanyController {
#Autowired private lateinit var userRepo: UserRepository
#RequestMapping(method = arrayOf(RequestMethod.POST))
#ResponseBody
fun createCompany(#RequestParam(value = "name", required = true) name: String,
#RequestParam(value = "siret", required = true) siret: Long) : ResponseEntity<String> {
val connectedUser = SecurityContextHolder.getContext().authentication.principal as User
val testUser = userRepo.findByEmail("test#company.com")
if (!PermissionManager().hasAuthorizationFor(PermissionManager.ADMIN_ALL, connectedUser))
return ResponseEntity("You're not authorized to create a company", HttpStatus.FORBIDDEN)
return ResponseEntity(HttpStatus.OK)
}
}
In the code above, the userRepo property is set well (aka. its value is not null).
I want to have this UserRepository in a custom service. So, I've coded:
#Service
open class PermissionManager {
companion object {
val ADMIN_ALL = "ADMIN_ALL"
}
#Autowired private lateinit var userRepo: UserRepository
fun hasAuthorizationFor(permissionName: String, user: User): Boolean {
return userRepo.authorizedUser(permissionName, user.id!!) != null
}
}
But in this case, the userRepo property is not initialized.
I've written the PersistenceContext class as:
#Configuration
#ComponentScan("persistence")
#EnableNeo4jRepositories("persistence.repositories")
#EnableTransactionManagement
open class PersistenceContext : Neo4jConfiguration() {
#Bean override fun getSessionFactory(): SessionFactory {
return SessionFactory("persistence.domain")
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
open override fun getSession(): Session {
return super.getSession()
}
}
What I don't understand is why is the userRepo set in my controller but not in my service ?

Resources