9

In a Spring Boot project I have a JPA entity, like this:

@Entity
public class Account {
}

and then I have the repository to query the database:

public interface AccountRepository extends JpaRepository<Account, UUID> {

}

In both the app and tests it's easy to get the repository by doing:

@Autowired
private AccountRepository accountRepository;

How can I get hold of the repository in a method in the Account class? I tried @Autowired but it didn't work.

For those arguing about design, my question is not about design. I have written this code in Ruby and Rails as well as Clojure with HugSQL and it was self contained and concise: when creating a new record I also generate a compact unique alphanumeric id. In Ruby on Rails I released it as a library: https://github.com/pupeno/random_unique_id

11
  • 3
    Are you sure there is not a better solution than accessing a repository from an entity? Commented Sep 7, 2017 at 9:30
  • @davioooh: yes, I'm pretty sure. Entities before being saved need to generate some data and that data depends on existing records. Commented Sep 7, 2017 at 9:38
  • 2
    ok... I think that entities should remain unaware about repositories. I personally prefer to implement extra logic in Service layer... but if are sure, go for this... Commented Sep 7, 2017 at 9:44
  • 1
    Possible duplicate of Autowired not working in a Class @Entity Commented Sep 7, 2017 at 9:48
  • 1
    @Pablo usually you put the extra logic in service layer of your application. But if you really need this i think you you could try @EntityListeners with@PrePersist concretepage.com/java/jpa/… Commented Sep 7, 2017 at 9:48

5 Answers 5

3

You can access the Spring Application Context from static method and use this static method to load your repository bean in your @Entity class instead of autowiring it.

You need to create the following classes (found here):

ApplicationContextProvider

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    public ApplicationContext getApplicationContext() {
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        context = ctx;
    }
}

SpringConfiguration

@Configuration
public class SpringConfiguration {

    @Bean
    public static ApplicationContextProvider contextProvider() {
        return new ApplicationContextProvider();
    }

}

And then

@Entity
public class Account {
    //your code

    public void doAccountRepositoryStuff() {
        AccountRepository accountRepository = (AccountRepository) SpringConfiguration.contextProvider().getApplicationContext().getBean("accountRepository");
        // Do your own stuff with accountRepository here...
    }
}
Sign up to request clarification or add additional context in comments.

Comments

3

I had same question and found this Github repository.

Here are some parts of code:

...
import de.ck35.example.ddd.jpa.SpringEntityListener;
...

@Entity
@Table(name="bookshelf")
@EntityListeners(SpringEntityListener.class)
public class BookshelfEntity implements Bookshelf {

    ...


    private String category;

    @Autowired transient BookRepository bookRepository;
    @Autowired transient BookshelfSpaceRepository bookshelfSpaceRepository;

Where de.ck35.example.ddd.jpa.SpringEntityListener purpose is described in Javadoc:

/**
 * Entity listener which allows dependency injection inside entities.
 * The listener can be registered via {@link EntityListeners} annotation.
 * 
 * Dependency injection annotations like {@link Autowired} are supported.
 * 
 * @author Christian Kaspari
 * @since 1.0.0
 */
public class SpringEntityListener {

    ...

    @PostLoad
    @PostPersist
    public void inject(Object object) {
        AutowireCapableBeanFactory beanFactory = get().getBeanFactory();
        if(beanFactory == null) {
            LOG.warn("Bean Factory not set! Depdendencies will not be injected into: '{}'", object);
            return;
        }
        LOG.debug("Injecting dependencies into entity: '{}'.", object);
        beanFactory.autowireBean(object);
    }

There is also configuration which enables Repositories definitions nested in classes (in this case entities):

@Configuration
@EnableJpaRepositories(basePackages="de.ck35.example.ddd.jpa", considerNestedRepositories=true)
@ComponentScan("de.ck35.example.ddd.jpa")
public class JpaConfiguration {

Thanks to Christian Kaspari who is author of this repository.

Comments

0

I'm guessing you are trying to implement something like the Active Record pattern in Hibernate. It's quite unusual but you could annotate your entity with @Configurable so you can get internally @Autowired to work (Also ensure entity package is within your @ComponentScan reach)

1 Comment

I'm not sure if I'm trying to implement active record, I'm just trying to generate an id.
0

You have to use @GenericGenerator and @GeneratedValue see an example here. Please notice that in the IdentifierGenerator implemntation you can access the repository.

If this is for non-id field, then the work around will be to make a new entity GeneralSequenceNumber and its ID is the required generated ID, then make one to one mapping between GeneralSequenceNumber and your Account entity (as mentioned here)

@Entity
public class GeneralSequenceNumber {
 @Id
 @GenericGenerator(name = "sequence_dep_id", strategy = "com.xyz.ids.DepartmentIdGenerator")
 @GeneratedValue(generator = "sequence_dep_id")  
 @Column(name="Department_Id")
 private String deptId;
}

@Entity 
public class Account {
  @Id ..
  private Long id;

  @OneToOne(...)
  private GeneralSequnceNumber myVal;
} 

3 Comments

Strategy can be a class? I missed that. Let me check that.
That was promising, but the generator seems to not be invoked. Are you sure it works for non-(primary-)id columns?
you have asked for 'unique alphanumeric id.' so I assumed that you want to do that for ID field.
-1

I saw the method below in a project because there were no Service layer at that project. To be honest, I recommend adding Service layer instead of these solutions but if you really want/have to use it, the solution is below.

Create Repository Interface

public interface BookRepository extends JpaRepository<Book, String> {
}

Create a Component

@Component
public class Registry {

private static BookRepository bookRepository;

static BookRepository bookRepo() {
    return bookRepository;
}

}

Achieve the repo by using static function in the Component

import static com.x.Registry.bookRepo;
@Entity
class Book {
    public static List<Book> all() {
        return bookRepo().findAll();
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.