First of all, we could use a middleware to make the sharding transparent to users for middle/large projects.
Here is a quick work around for my small project (and I'm the only developer working it):
Step 1, create an inteceptor:
public class MySqlInterceptor extends EmptyInterceptor {
    private String entityName;
    @Setter
    private int tableId;
    protected MySqlInterceptor() {
        super();
    }
    public MySqlInterceptor(String entityName) {
        this.entityName = entityName;
    }
    @Override
    public String onPrepareStatement(String sql) {
        // Here is the trick.
        String modifiedSql = sql.replaceAll(entityName, entityName + tableId);
        log.debug("{}", modifiedSql);
        return modifiedSql;
    }
}
Step 2, hock up the interceptor:
MySqlInterceptor mySqlInterceptor = new MySqlInterceptor("temp");
                mySqlInterceptor.setTableId(tableId);
                session = entityManagerFactory.unwrap(SessionFactory.class).withOptions().interceptor(mySqlInterceptor).openSession();
Explanation:
Hibernate is using JDBC to communicate with the database. The interceptor will change the table name in the sql from an entity's name (in my case it's temp) to a real table name (temp1, temp2, ...) at runtime.
P.S., be careful when you use multi-thread.